This commit is contained in:
Hykilpikonna
2021-12-07 22:28:01 -05:00
commit 6fffdf686a
151 changed files with 37985 additions and 0 deletions
+317
View File
@@ -0,0 +1,317 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>4.1 Tabular Data</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="../tufte.css" />
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" type="text/javascript"></script>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<div style="display:none">
\(
\newcommand{\NOT}{\neg}
\newcommand{\AND}{\wedge}
\newcommand{\OR}{\vee}
\newcommand{\XOR}{\oplus}
\newcommand{\IMP}{\Rightarrow}
\newcommand{\IFF}{\Leftrightarrow}
\newcommand{\TRUE}{\text{True}\xspace}
\newcommand{\FALSE}{\text{False}\xspace}
\newcommand{\IN}{\,{\in}\,}
\newcommand{\NOTIN}{\,{\notin}\,}
\newcommand{\TO}{\rightarrow}
\newcommand{\DIV}{\mid}
\newcommand{\NDIV}{\nmid}
\newcommand{\MOD}[1]{\pmod{#1}}
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
\newcommand{\N}{\mathbb N}
\newcommand{\Z}{\mathbb Z}
\newcommand{\Q}{\mathbb Q}
\newcommand{\R}{\mathbb R}
\newcommand{\C}{\mathbb C}
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
\newcommand{\cD}{\mathcal D}
\newcommand{\cE}{\mathcal E}
\newcommand{\cF}{\mathcal F}
\newcommand{\cG}{\mathcal G}
\newcommand{\cH}{\mathcal H}
\newcommand{\cI}{\mathcal I}
\newcommand{\cJ}{\mathcal J}
\newcommand{\cL}{\mathcal L}
\newcommand{\cK}{\mathcal K}
\newcommand{\cN}{\mathcal N}
\newcommand{\cO}{\mathcal O}
\newcommand{\cP}{\mathcal P}
\newcommand{\cQ}{\mathcal Q}
\newcommand{\cS}{\mathcal S}
\newcommand{\cT}{\mathcal T}
\newcommand{\cV}{\mathcal V}
\newcommand{\cW}{\mathcal W}
\newcommand{\cZ}{\mathcal Z}
\newcommand{\emp}{\emptyset}
\newcommand{\bs}{\backslash}
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
\newcommand{\abs}[1]{\left | #1 \right |}
\newcommand{\xspace}{}
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
\)
</div>
<header id="title-block-header">
<h1 class="title">4.1 Tabular Data</h1>
</header>
<section>
<p>Weve seen how Python can store collections of data, such as lists, sets, and dictionaries. Mostly, weve focused on collections of integers or strings. But what about collections of collections? Weve actually encountered this already: our <a href="../03-logic/04-if-statements.html"><code>count_cancelled</code> function</a> had a parameter <code>flights</code> that was a dictionary whose values were lists, and we represented the <span class="math inline">\(Loves\)</span> predicate as a <a href="../03-logic/11-multiple-quantifiers.html">list of lists</a>, storing a two-dimensional table of booleans. In this section, well look at using list of lists to store more complex forms of tabular data, like a table from a spreadsheet, and writing functions to perform computations on this data.</p>
<h2 id="toronto-getting-married">Toronto getting married</h2>
<p>Lets consider a <a href="https://open.toronto.ca/dataset/marriage-licence-statistics/">real data set</a> from the city of Toronto. This data shows information about how many marriage licenses were issued in Toronto at a particular location and month. The data is in a tabular format with four columns: id, civic centre, number of marriage licenses issued, and time period. Each row of the table tells us how many marriage licenses were issued by a civic centre in a specific time period; the id is simply a unique numerical identifier for each row. Suppose we wanted to answer the following question: What is the average number of marriage licenses issued by each civic centre?</p>
<table>
<thead>
<tr class="header">
<th style="text-align: center;"><strong>ID</strong></th>
<th style="text-align: center;"><strong>Civic Centre</strong></th>
<th style="text-align: center;"><strong>Marriage Licenses Issued</strong></th>
<th style="text-align: center;"><strong>Time Period</strong></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: center;">1657</td>
<td style="text-align: center;">ET</td>
<td style="text-align: center;">80</td>
<td style="text-align: center;">January 1, 2011</td>
</tr>
<tr class="even">
<td style="text-align: center;">1658</td>
<td style="text-align: center;">NY</td>
<td style="text-align: center;">136</td>
<td style="text-align: center;">January 1, 2011</td>
</tr>
<tr class="odd">
<td style="text-align: center;">1659</td>
<td style="text-align: center;">SC</td>
<td style="text-align: center;">159</td>
<td style="text-align: center;">January 1, 2011</td>
</tr>
<tr class="even">
<td style="text-align: center;">1660</td>
<td style="text-align: center;">TO</td>
<td style="text-align: center;">367</td>
<td style="text-align: center;">January 1, 2011</td>
</tr>
<tr class="odd">
<td style="text-align: center;">1661</td>
<td style="text-align: center;">ET</td>
<td style="text-align: center;">109</td>
<td style="text-align: center;">February 1, 2011</td>
</tr>
<tr class="even">
<td style="text-align: center;">1662</td>
<td style="text-align: center;">NY</td>
<td style="text-align: center;">150</td>
<td style="text-align: center;">February 1, 2011</td>
</tr>
<tr class="odd">
<td style="text-align: center;">1663</td>
<td style="text-align: center;">SC</td>
<td style="text-align: center;">154</td>
<td style="text-align: center;">February 1, 2011</td>
</tr>
<tr class="even">
<td style="text-align: center;">1664</td>
<td style="text-align: center;">TO</td>
<td style="text-align: center;">383</td>
<td style="text-align: center;">February 1, 2011</td>
</tr>
</tbody>
</table>
<p>To write a program that uses this data, we must first decide on a way to store it. As we did with our <span class="math inline">\(Loves\)</span> table of values, well store this table as a list of lists, where each inner list represents one row of the table. Unlike our previous example, these lists wont just store boolean values, so we need to determine what data type to use for each column, based on the sample data we have.</p>
<ul>
<li>The ids and number of marriage licenses are natural numbers, so well use the <code>int</code> data type for them.</li>
<li>The civic centre is a two-letter code, and so well store it as a <code>str</code>.</li>
<li>The time period is a year-month combination; well represent these as dates using the <code>datetime</code> module.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> To review this <code>date</code> data type, check out <a href="../02-functions/04-importing-modules.html">2.4 Importing Modules</a>.</span></li>
</ul>
<p>With this in mind, let us see how we can store our data as a nested list<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote"> In tutorial, you will explore how to load the data from a file into a nested list.</span>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="op">&gt;&gt;&gt;</span> <span class="im">import</span> datetime</span>
<span id="cb1-2"><a href="#cb1-2"></a><span class="op">&gt;&gt;&gt;</span> marriage_data <span class="op">=</span> [</span>
<span id="cb1-3"><a href="#cb1-3"></a>... [<span class="dv">1657</span>, <span class="st">&#39;ET&#39;</span>, <span class="dv">80</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)],</span>
<span id="cb1-4"><a href="#cb1-4"></a>... [<span class="dv">1658</span>, <span class="st">&#39;NY&#39;</span>, <span class="dv">136</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)],</span>
<span id="cb1-5"><a href="#cb1-5"></a>... [<span class="dv">1659</span>, <span class="st">&#39;SC&#39;</span>, <span class="dv">159</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)],</span>
<span id="cb1-6"><a href="#cb1-6"></a>... [<span class="dv">1660</span>, <span class="st">&#39;TO&#39;</span>, <span class="dv">367</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)],</span>
<span id="cb1-7"><a href="#cb1-7"></a>... [<span class="dv">1661</span>, <span class="st">&#39;ET&#39;</span>, <span class="dv">109</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)],</span>
<span id="cb1-8"><a href="#cb1-8"></a>... [<span class="dv">1662</span>, <span class="st">&#39;NY&#39;</span>, <span class="dv">150</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)],</span>
<span id="cb1-9"><a href="#cb1-9"></a>... [<span class="dv">1663</span>, <span class="st">&#39;SC&#39;</span>, <span class="dv">154</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)],</span>
<span id="cb1-10"><a href="#cb1-10"></a>... [<span class="dv">1664</span>, <span class="st">&#39;TO&#39;</span>, <span class="dv">383</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)]</span>
<span id="cb1-11"><a href="#cb1-11"></a>... ]</span>
<span id="cb1-12"><a href="#cb1-12"></a><span class="op">&gt;&gt;&gt;</span> <span class="bu">len</span>(marriage_data) <span class="co"># There are eight rows of data</span></span>
<span id="cb1-13"><a href="#cb1-13"></a><span class="dv">8</span></span>
<span id="cb1-14"><a href="#cb1-14"></a><span class="op">&gt;&gt;&gt;</span> <span class="bu">len</span>(marriage_data[<span class="dv">0</span>]) <span class="co"># The first row has four elements</span></span>
<span id="cb1-15"><a href="#cb1-15"></a><span class="dv">4</span></span>
<span id="cb1-16"><a href="#cb1-16"></a><span class="op">&gt;&gt;&gt;</span> [<span class="bu">len</span>(row) <span class="cf">for</span> row <span class="kw">in</span> marriage_data] <span class="co"># Every row has four elements</span></span>
<span id="cb1-17"><a href="#cb1-17"></a>[<span class="dv">4</span>, <span class="dv">4</span>, <span class="dv">4</span>, <span class="dv">4</span>, <span class="dv">4</span>, <span class="dv">4</span>, <span class="dv">4</span>, <span class="dv">4</span>]</span>
<span id="cb1-18"><a href="#cb1-18"></a><span class="op">&gt;&gt;&gt;</span> marriage_data[<span class="dv">0</span>]</span>
<span id="cb1-19"><a href="#cb1-19"></a>[<span class="dv">1657</span>, <span class="st">&#39;ET&#39;</span>, <span class="dv">80</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)]</span>
<span id="cb1-20"><a href="#cb1-20"></a><span class="op">&gt;&gt;&gt;</span> marriage_data[<span class="dv">1</span>]</span>
<span id="cb1-21"><a href="#cb1-21"></a>[<span class="dv">1658</span>, <span class="st">&#39;NY&#39;</span>, <span class="dv">136</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)]</span></code></pre></div>
<p>We can see that by indexing the nested list <code>marriage_data</code>, a list is returned. Specifically, this list represents a row from our table. For each row, we can then access its id via index 0, its civic centre via index 1, and so on.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="op">&gt;&gt;&gt;</span> marriage_data[<span class="dv">0</span>][<span class="dv">0</span>]</span>
<span id="cb2-2"><a href="#cb2-2"></a><span class="dv">1657</span></span>
<span id="cb2-3"><a href="#cb2-3"></a><span class="op">&gt;&gt;&gt;</span> marriage_data[<span class="dv">0</span>][<span class="dv">1</span>]</span>
<span id="cb2-4"><a href="#cb2-4"></a><span class="co">&#39;ET&#39;</span></span>
<span id="cb2-5"><a href="#cb2-5"></a><span class="op">&gt;&gt;&gt;</span> marriage_data[<span class="dv">0</span>][<span class="dv">2</span>]</span>
<span id="cb2-6"><a href="#cb2-6"></a><span class="dv">80</span></span>
<span id="cb2-7"><a href="#cb2-7"></a><span class="op">&gt;&gt;&gt;</span> marriage_data[<span class="dv">0</span>][<span class="dv">3</span>]</span>
<span id="cb2-8"><a href="#cb2-8"></a>datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)</span></code></pre></div>
<h2 id="accessing-columns-and-filtering-rows">Accessing columns and filtering rows</h2>
<p>Suppose we want to see all of the different values from a single column of this table (e.g., all civic centres or marriage license numbers). We can retrieve a column by using a list comprehension:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="op">&gt;&gt;&gt;</span> [row[<span class="dv">1</span>] <span class="cf">for</span> row <span class="kw">in</span> marriage_data] <span class="co"># The civic centre column</span></span>
<span id="cb3-2"><a href="#cb3-2"></a>[<span class="st">&#39;ET&#39;</span>, <span class="st">&#39;NY&#39;</span>, <span class="st">&#39;SC&#39;</span>, <span class="st">&#39;TO&#39;</span>, <span class="st">&#39;ET&#39;</span>, <span class="st">&#39;NY&#39;</span>, <span class="st">&#39;SC&#39;</span>, <span class="st">&#39;TO&#39;</span>]</span></code></pre></div>
<p>Or, using an identically-structured set comprehension, we can obtain all unique values in a column.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="op">&gt;&gt;&gt;</span> {row[<span class="dv">1</span>] <span class="cf">for</span> row <span class="kw">in</span> marriage_data}</span>
<span id="cb4-2"><a href="#cb4-2"></a>{<span class="st">&#39;NY&#39;</span>, <span class="st">&#39;TO&#39;</span>, <span class="st">&#39;ET&#39;</span>, <span class="st">&#39;SC&#39;</span>}</span></code></pre></div>
<p>Using our knowledge of filtering using if conditions in comprehensions, we can retrieve all rows corresponding to a specific civic centre.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="op">&gt;&gt;&gt;</span> [row <span class="cf">for</span> row <span class="kw">in</span> marriage_data <span class="cf">if</span> row[<span class="dv">1</span>] <span class="op">==</span> <span class="st">&#39;TO&#39;</span>]</span>
<span id="cb5-2"><a href="#cb5-2"></a>[[<span class="dv">1660</span>, <span class="st">&#39;TO&#39;</span>, <span class="dv">367</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)], [<span class="dv">1664</span>, <span class="st">&#39;TO&#39;</span>, <span class="dv">383</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)]]</span></code></pre></div>
<p>Or we can filter rows based on a threshold for the number of marriage licenses issued:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="op">&gt;&gt;&gt;</span> [row <span class="cf">for</span> row <span class="kw">in</span> marriage_data <span class="cf">if</span> row[<span class="dv">2</span>] <span class="op">&gt;</span> <span class="dv">380</span>]</span>
<span id="cb6-2"><a href="#cb6-2"></a>[[<span class="dv">1664</span>, <span class="st">&#39;TO&#39;</span>, <span class="dv">383</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)]]</span></code></pre></div>
<h2 id="a-worked-example">A worked example</h2>
<p>Earlier, we asked the question: What is the average number of marriage licenses issued by each civic centre? The question implies a mapping of civic centre names to numbers (i.e., the average). This means we need to create a dictionary comprehension. Lets start exploring in the Python console. Remember, we saw earlier that we can get all unique civic centre names in the data through a set comprehension.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="op">&gt;&gt;&gt;</span> names <span class="op">=</span> {row[<span class="dv">1</span>] <span class="cf">for</span> row <span class="kw">in</span> marriage_data}</span>
<span id="cb7-2"><a href="#cb7-2"></a><span class="op">&gt;&gt;&gt;</span> names</span>
<span id="cb7-3"><a href="#cb7-3"></a>{<span class="st">&#39;NY&#39;</span>, <span class="st">&#39;TO&#39;</span>, <span class="st">&#39;ET&#39;</span>, <span class="st">&#39;SC&#39;</span>}</span>
<span id="cb7-4"><a href="#cb7-4"></a><span class="op">&gt;&gt;&gt;</span> {key: <span class="dv">0</span> <span class="cf">for</span> key <span class="kw">in</span> names}</span>
<span id="cb7-5"><a href="#cb7-5"></a>{<span class="st">&#39;NY&#39;</span>: <span class="dv">0</span>, <span class="st">&#39;TO&#39;</span>: <span class="dv">0</span>, <span class="st">&#39;ET&#39;</span>: <span class="dv">0</span>, <span class="st">&#39;SC&#39;</span>: <span class="dv">0</span>}</span></code></pre></div>
<p>So far, weve created a dictionary where each key is a civic centre name and they all map to the value 0. To proceed, we need to be able to calculate the average number of marriage licenses issued per month by each civic centre.</p>
<p>Lets try to do this just for the <code>'TO'</code> civic centre first. We saw earlier how to get all rows for a specific civic centre, and to extract the values for a specific column. Well first combine these two operations to retrieve the number of marriage licenses issued by <code>'TO'</code> each month.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1"></a><span class="op">&gt;&gt;&gt;</span> [row <span class="cf">for</span> row <span class="kw">in</span> marriage_data <span class="cf">if</span> row[<span class="dv">1</span>] <span class="op">==</span> <span class="st">&#39;TO&#39;</span>] <span class="co"># The &#39;TO&#39; rows</span></span>
<span id="cb8-2"><a href="#cb8-2"></a>[[<span class="dv">1660</span>, <span class="st">&#39;TO&#39;</span>, <span class="dv">367</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)], [<span class="dv">1664</span>, <span class="st">&#39;TO&#39;</span>, <span class="dv">383</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)]]</span>
<span id="cb8-3"><a href="#cb8-3"></a><span class="op">&gt;&gt;&gt;</span> [row[<span class="dv">2</span>] <span class="cf">for</span> row <span class="kw">in</span> marriage_data <span class="cf">if</span> row[<span class="dv">1</span>] <span class="op">==</span> <span class="st">&#39;TO&#39;</span>] <span class="co"># The &#39;TO&#39; marriages issued</span></span>
<span id="cb8-4"><a href="#cb8-4"></a>[<span class="dv">367</span>, <span class="dv">383</span>]</span>
<span id="cb8-5"><a href="#cb8-5"></a><span class="op">&gt;&gt;&gt;</span> issued_by_TO <span class="op">=</span> [row[<span class="dv">2</span>] <span class="cf">for</span> row <span class="kw">in</span> marriage_data <span class="cf">if</span> row[<span class="dv">1</span>] <span class="op">==</span> <span class="st">&#39;TO&#39;</span>]</span></code></pre></div>
<p>So <code>issued_by_TO</code> is now a list containing the number of marriage licenses issued by the <code>'TO'</code> civic centre. We can now calculate their average by dividing its sum by its length:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1"></a><span class="op">&gt;&gt;&gt;</span> <span class="bu">sum</span>(issued_by_TO) <span class="op">/</span> <span class="bu">len</span>(issued_by_TO)</span>
<span id="cb9-2"><a href="#cb9-2"></a><span class="fl">375.0</span></span></code></pre></div>
<p>Excellent! Through our exploration, we managed to find the average number of marriage licenses issued by one specific civic centre. How can we merge this with our earlier dictionary comprehension? Its quite a bit to keep in our head at once, and looks like it will quickly get messy. At this point, we should design a function to help us. Specifically, lets design a function that calculates the average for only one civic centre. As input, we will need the dataset as well as the name of the civic centre we are querying.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1"></a><span class="kw">def</span> average_licenses_issued(data: <span class="bu">list</span>[<span class="bu">list</span>], civic_centre: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">float</span>:</span>
<span id="cb10-2"><a href="#cb10-2"></a> <span class="co">&quot;&quot;&quot;Return the average number of marriage licenses issued by civic_centre in data.</span></span>
<span id="cb10-3"><a href="#cb10-3"></a></span>
<span id="cb10-4"><a href="#cb10-4"></a><span class="co"> Return 0.0 if civic_centre does not appear in the given data.</span></span>
<span id="cb10-5"><a href="#cb10-5"></a></span>
<span id="cb10-6"><a href="#cb10-6"></a><span class="co"> Preconditions:</span></span>
<span id="cb10-7"><a href="#cb10-7"></a><span class="co"> - all({len(row) == 4 for row in data})</span></span>
<span id="cb10-8"><a href="#cb10-8"></a><span class="co"> - data is in the format described in Section 4.1</span></span>
<span id="cb10-9"><a href="#cb10-9"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb10-10"><a href="#cb10-10"></a> issued_by_civic_centre <span class="op">=</span> [row[<span class="dv">2</span>] <span class="cf">for</span> row <span class="kw">in</span> data <span class="cf">if</span> row[<span class="dv">1</span>] <span class="op">==</span> civic_centre]</span>
<span id="cb10-11"><a href="#cb10-11"></a></span>
<span id="cb10-12"><a href="#cb10-12"></a> <span class="cf">if</span> issued_by_civic_centre <span class="op">==</span> []:</span>
<span id="cb10-13"><a href="#cb10-13"></a> <span class="cf">return</span> <span class="fl">0.0</span></span>
<span id="cb10-14"><a href="#cb10-14"></a> <span class="cf">else</span>:</span>
<span id="cb10-15"><a href="#cb10-15"></a> total <span class="op">=</span> <span class="bu">sum</span>(issued_by_civic_centre)</span>
<span id="cb10-16"><a href="#cb10-16"></a> count <span class="op">=</span> <span class="bu">len</span>(issued_by_civic_centre)</span>
<span id="cb10-17"><a href="#cb10-17"></a></span>
<span id="cb10-18"><a href="#cb10-18"></a> <span class="cf">return</span> total <span class="op">/</span> count</span></code></pre></div>
<p>Lets test it to make sure we get the same result as before:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1"></a><span class="op">&gt;&gt;&gt;</span> average_licenses_issued(marriage_data, <span class="st">&#39;TO&#39;</span>)</span>
<span id="cb11-2"><a href="#cb11-2"></a><span class="fl">375.0</span></span></code></pre></div>
<p>Finally, we can combine it with our previous dictionary comprehension by observing that <code>'TO'</code> can be replaced with the <code>key</code> that is changing:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1"></a><span class="op">&gt;&gt;&gt;</span> {key: <span class="dv">0</span> <span class="cf">for</span> key <span class="kw">in</span> names}</span>
<span id="cb12-2"><a href="#cb12-2"></a>{<span class="st">&#39;NY&#39;</span>: <span class="dv">0</span>, <span class="st">&#39;TO&#39;</span>: <span class="dv">0</span>, <span class="st">&#39;ET&#39;</span>: <span class="dv">0</span>, <span class="st">&#39;SC&#39;</span>: <span class="dv">0</span>}</span>
<span id="cb12-3"><a href="#cb12-3"></a><span class="op">&gt;&gt;&gt;</span> {key: average_licenses_issued(marriage_data, key) <span class="cf">for</span> key <span class="kw">in</span> names}</span>
<span id="cb12-4"><a href="#cb12-4"></a>{<span class="st">&#39;NY&#39;</span>: <span class="fl">143.0</span>, <span class="st">&#39;TO&#39;</span>: <span class="fl">375.0</span>, <span class="st">&#39;ET&#39;</span>: <span class="fl">94.5</span>, <span class="st">&#39;SC&#39;</span>: <span class="fl">156.5</span>}</span></code></pre></div>
<p>Now that weve done this exploration in the Python console, we can save our work by writing this as a function:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1"></a><span class="kw">def</span> average_licenses_by_centre(marriage_data: <span class="bu">list</span>[<span class="bu">list</span>]) <span class="op">-&gt;</span> Dict[<span class="bu">str</span>, <span class="bu">float</span>]:</span>
<span id="cb13-2"><a href="#cb13-2"></a> <span class="co">&quot;&quot;&quot;Return a mapping of the average number of marriage licenses issued at each civic centre.</span></span>
<span id="cb13-3"><a href="#cb13-3"></a></span>
<span id="cb13-4"><a href="#cb13-4"></a><span class="co"> In the returned mapping:</span></span>
<span id="cb13-5"><a href="#cb13-5"></a><span class="co"> - Each key is the name of a civic centre</span></span>
<span id="cb13-6"><a href="#cb13-6"></a><span class="co"> - Each corresponding value is the average number of marriage licenses issued at</span></span>
<span id="cb13-7"><a href="#cb13-7"></a><span class="co"> that centre.</span></span>
<span id="cb13-8"><a href="#cb13-8"></a></span>
<span id="cb13-9"><a href="#cb13-9"></a><span class="co"> Preconditions:</span></span>
<span id="cb13-10"><a href="#cb13-10"></a><span class="co"> - marriage_data is in the format described in Section 4.1</span></span>
<span id="cb13-11"><a href="#cb13-11"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb13-12"><a href="#cb13-12"></a> names <span class="op">=</span> {<span class="st">&#39;TO&#39;</span>, <span class="st">&#39;NY&#39;</span>, <span class="st">&#39;ET&#39;</span>, <span class="st">&#39;SC&#39;</span>}</span>
<span id="cb13-13"><a href="#cb13-13"></a> <span class="cf">return</span> {key: average_licenses_issued(marriage_data, key) <span class="cf">for</span> key <span class="kw">in</span> names}</span></code></pre></div>
</section>
<footer>
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
</footer>
</body>
</html>
+262
View File
@@ -0,0 +1,262 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>4.2 Defining Our Own Data Types, Part 1</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="../tufte.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<div style="display:none">
\(
\newcommand{\NOT}{\neg}
\newcommand{\AND}{\wedge}
\newcommand{\OR}{\vee}
\newcommand{\XOR}{\oplus}
\newcommand{\IMP}{\Rightarrow}
\newcommand{\IFF}{\Leftrightarrow}
\newcommand{\TRUE}{\text{True}\xspace}
\newcommand{\FALSE}{\text{False}\xspace}
\newcommand{\IN}{\,{\in}\,}
\newcommand{\NOTIN}{\,{\notin}\,}
\newcommand{\TO}{\rightarrow}
\newcommand{\DIV}{\mid}
\newcommand{\NDIV}{\nmid}
\newcommand{\MOD}[1]{\pmod{#1}}
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
\newcommand{\N}{\mathbb N}
\newcommand{\Z}{\mathbb Z}
\newcommand{\Q}{\mathbb Q}
\newcommand{\R}{\mathbb R}
\newcommand{\C}{\mathbb C}
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
\newcommand{\cD}{\mathcal D}
\newcommand{\cE}{\mathcal E}
\newcommand{\cF}{\mathcal F}
\newcommand{\cG}{\mathcal G}
\newcommand{\cH}{\mathcal H}
\newcommand{\cI}{\mathcal I}
\newcommand{\cJ}{\mathcal J}
\newcommand{\cL}{\mathcal L}
\newcommand{\cK}{\mathcal K}
\newcommand{\cN}{\mathcal N}
\newcommand{\cO}{\mathcal O}
\newcommand{\cP}{\mathcal P}
\newcommand{\cQ}{\mathcal Q}
\newcommand{\cS}{\mathcal S}
\newcommand{\cT}{\mathcal T}
\newcommand{\cV}{\mathcal V}
\newcommand{\cW}{\mathcal W}
\newcommand{\cZ}{\mathcal Z}
\newcommand{\emp}{\emptyset}
\newcommand{\bs}{\backslash}
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
\newcommand{\abs}[1]{\left | #1 \right |}
\newcommand{\xspace}{}
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
\)
</div>
<header id="title-block-header">
<h1 class="title">4.2 Defining Our Own Data Types, Part 1</h1>
</header>
<section>
<p>Up to this point, all the data weve worked with in Python have been stored in objects that are instances of the built-in types that come with Python, like <code>int</code>s and <code>list</code>s. Pythons built-in data types are powerful, but are not always the most intuitive way to store data. For example, we saw in <a href="01-tabular-data.html">4.1 Tabular Data</a> that we could use a list of lists to represent tabular data. One of the downsides of this approach is that when working with this data, the onus is on us to remember which list element corresponds to which component of the data.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="op">&gt;&gt;&gt;</span> <span class="im">import</span> datetime</span>
<span id="cb1-2"><a href="#cb1-2"></a><span class="op">&gt;&gt;&gt;</span> row <span class="op">=</span> [<span class="dv">1657</span>, <span class="st">&#39;ET&#39;</span>, <span class="dv">80</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)]</span>
<span id="cb1-3"><a href="#cb1-3"></a><span class="op">&gt;&gt;&gt;</span> row[<span class="dv">0</span>] <span class="co"># The id</span></span>
<span id="cb1-4"><a href="#cb1-4"></a><span class="dv">1657</span></span>
<span id="cb1-5"><a href="#cb1-5"></a><span class="op">&gt;&gt;&gt;</span> row[<span class="dv">1</span>] <span class="co"># The name of the civic centre</span></span>
<span id="cb1-6"><a href="#cb1-6"></a><span class="co">&#39;ET&#39;</span></span>
<span id="cb1-7"><a href="#cb1-7"></a><span class="op">&gt;&gt;&gt;</span> row[<span class="dv">2</span>] <span class="co"># The number of marriage licenses issued</span></span>
<span id="cb1-8"><a href="#cb1-8"></a><span class="dv">80</span></span>
<span id="cb1-9"><a href="#cb1-9"></a><span class="op">&gt;&gt;&gt;</span> row[<span class="dv">3</span>] <span class="co"># The time period</span></span>
<span id="cb1-10"><a href="#cb1-10"></a>datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)</span></code></pre></div>
<p>You can imagine how error prone this might be. A simple “off by one” error for an index might retrieve a completely different data type. It also makes our code difficult to read; the reader must know what each index of the list represents. And, as more experienced programmers will tell you, readable code is crucial.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> “Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” Martin Fowler</span></p>
<p>So a row in our marriage license data set is made up of four data elements. It would be nice if, instead of indices, we could use a name that was reflective of each element. Certainly, we could use a dictionary (instead of a list) where the keys are strings. But there is a more robust option well learn about in this section: creating our <em>own</em> data types.</p>
<h2 id="defining-a-data-class">Defining a data class</h2>
<p>You might remember from Chapter 1 that in Python, another term for data type is a <strong>class</strong>.<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote"> This is why <code>type(3)</code> evaluates to <code>&lt;class 'int'&gt;</code> in Python.</span> The built-in data types weve studied so far illustrate how rich and complex data types can be. So for creating our own data types, we will first learn about the simplest kind of data type: a <strong>data class</strong>, which is a kind of class whose purpose is to bundle individual pieces of data into a single Python object.</p>
<p>For example, suppose we want to represent a “person” consisting of a given name, family name, age, and home address. We already know how to represent each individual piece of data: the given name, family name, and address could be strings, and the age could be a natural number. To bundle these values together, we could use a list or other built-in collection data type, but that approach would run into the issues we discussed above.</p>
<p>So instead, we define our own data class to create a new data type consisting of these four values. Here is the way to create a data class in Python:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="im">from</span> dataclasses <span class="im">import</span> dataclass</span>
<span id="cb2-2"><a href="#cb2-2"></a></span>
<span id="cb2-3"><a href="#cb2-3"></a></span>
<span id="cb2-4"><a href="#cb2-4"></a><span class="at">@dataclass</span></span>
<span id="cb2-5"><a href="#cb2-5"></a><span class="kw">class</span> Person:</span>
<span id="cb2-6"><a href="#cb2-6"></a> <span class="co">&quot;&quot;&quot;A custom data type that represents data for a person.&quot;&quot;&quot;</span></span>
<span id="cb2-7"><a href="#cb2-7"></a> given_name: <span class="bu">str</span></span>
<span id="cb2-8"><a href="#cb2-8"></a> family_name: <span class="bu">str</span></span>
<span id="cb2-9"><a href="#cb2-9"></a> age: <span class="bu">int</span></span>
<span id="cb2-10"><a href="#cb2-10"></a> address: <span class="bu">str</span></span></code></pre></div>
<p>Lets unpack this definition.</p>
<ol type="1">
<li><p><code>from dataclasses import dataclass</code> is a Python import statement that lets us use <code>dataclass</code> below.</p></li>
<li><p><code>@dataclass</code> is a Python <em>decorator</em>. Weve seen decorators before for function definitions; a decorator for a class definition works in the same way, acting as a modifier for our definition. In this case, <code>@dataclass</code> tells Python that the data type were defining is a data class, which well explore the benefits of down below.</p></li>
<li><p><code>class Person:</code>, signals the start of a <em>class definition</em>.<label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote"> This is similar to function definitions, except we use the <code>class</code> keyword instead of <code>def</code>. </span> The name of the class is <code>Person</code>.</p>
<p>The rest of the code is indented to put it inside of the class body.</p></li>
<li><p>The next line is a docstring that describes the purpose of the class.</p></li>
<li><p>Each remaining line (starting with <code>given_name: str</code>) defines a piece of data associated with the class; each piece of data is called an <strong>instance attribute</strong> of the class.</p>
<p>For each instance attribute, we write a name and a type annotation.<label for="sn-3" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-3" class="margin-toggle"/><span class="sidenote"> This is similar to defining parameter names and types for functions, though of course the purposes are different. </span></p></li>
</ol>
<h3 id="general-data-class-definition-syntax">General data class definition syntax</h3>
<p>In general, a data class definition in Python has the following syntax:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="at">@dataclass</span></span>
<span id="cb3-2"><a href="#cb3-2"></a><span class="kw">class</span> <span class="op">&lt;</span>ClassName<span class="op">&gt;</span>:</span>
<span id="cb3-3"><a href="#cb3-3"></a> <span class="co">&quot;&quot;&quot;Description of data class.</span></span>
<span id="cb3-4"><a href="#cb3-4"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb3-5"><a href="#cb3-5"></a> <span class="op">&lt;</span>attribute1<span class="op">&gt;</span>: <span class="op">&lt;</span>type1<span class="op">&gt;</span></span>
<span id="cb3-6"><a href="#cb3-6"></a> <span class="op">&lt;</span>attribute2<span class="op">&gt;</span>: <span class="op">&lt;</span>type2<span class="op">&gt;</span></span>
<span id="cb3-7"><a href="#cb3-7"></a> ...</span></code></pre></div>
<h2 id="using-data-classes">Using data classes</h2>
<p>Now that weve seen how to define a data class, we now are ready to actually put it to use. To create an instance of our <code>Person</code> data class, we write a Python expression that calls the data class, passing in as arguments the values for each instance attribute:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="op">&gt;&gt;&gt;</span> david <span class="op">=</span> Person(<span class="st">&#39;David&#39;</span>, <span class="st">&#39;Liu&#39;</span>, <span class="dv">100</span>, <span class="st">&#39;40 St. George Street&#39;</span>)</span></code></pre></div>
<p>Pretty cool! That line of code creates a new <code>Person</code> object whose given name is <code>'David'</code>, family name is <code>'Liu'</code>, age is <code>100</code>, and address is <code>'40 St. George Street'</code>, and stores the object in the variable <code>david</code>. The <em>type</em> of this new value is, as wed expect, <code>Person</code>:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="op">&gt;&gt;&gt;</span> <span class="bu">type</span>(david)</span>
<span id="cb5-2"><a href="#cb5-2"></a><span class="op">&lt;</span><span class="kw">class</span> Person<span class="op">&gt;</span></span></code></pre></div>
<p>If we ask Python to evaluate the <code>Person</code> object, we see the different pieces of data that have been bundled together:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="op">&gt;&gt;&gt;</span> david</span>
<span id="cb6-2"><a href="#cb6-2"></a>Person(given_name<span class="op">=</span><span class="st">&#39;David&#39;</span>, family_name<span class="op">=</span><span class="st">&#39;Liu&#39;</span>, age<span class="op">=</span><span class="dv">100</span>, address<span class="op">=</span><span class="st">&#39;40 St. George Street&#39;</span>)</span></code></pre></div>
<p>But from a <code>Person</code> object, how do we extract the individual values we bundled together? If we were using lists, wed simply do list indexing: <code>david[0]</code>, <code>david[1]</code>, etc. The syntax for Python classes improves this because we can use the names of the instance attributes together with <strong>dot notation</strong> to access these values:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="op">&gt;&gt;&gt;</span> david.given_name</span>
<span id="cb7-2"><a href="#cb7-2"></a><span class="co">&#39;David&#39;</span></span>
<span id="cb7-3"><a href="#cb7-3"></a><span class="op">&gt;&gt;&gt;</span> david.family_name</span>
<span id="cb7-4"><a href="#cb7-4"></a><span class="co">&#39;Liu&#39;</span></span>
<span id="cb7-5"><a href="#cb7-5"></a><span class="op">&gt;&gt;&gt;</span> david.age</span>
<span id="cb7-6"><a href="#cb7-6"></a><span class="dv">100</span></span>
<span id="cb7-7"><a href="#cb7-7"></a><span class="op">&gt;&gt;&gt;</span> david.address</span>
<span id="cb7-8"><a href="#cb7-8"></a><span class="co">&#39;40 St. George Street&#39;</span></span></code></pre></div>
<p>This is much more readable than list indexing, and this is one of the major advantages of using data classes over lists to represent custom data in Python.</p>
<h2 id="tip-naming-attributes-when-creating-data-class-instances">Tip: naming attributes when creating data class instances</h2>
<p>One challenge when creating instances of our data classes is keeping track of which arguments correspond to which instance attributes. In the expression <code>Person('David', 'Liu', 100, '40 St. George Street')</code>, the order of the arguments must match the order the instance attributes are listed in the definition of the data class—and its our responsibility to remember this order. Think about how easy it would be for us to write <code>Person('Liu', 'David', 100, '40 St. George Street')</code>, only to discover much later in our program that we accidentally switched this poor fellows given and family names!</p>
<p>To solve this issue, Python enables us to create data class instances using <em>keyword arguments</em> to explicitly name which argument corresponds to which instance attribute, using the exact same format as the <code>Person</code> representation we saw above:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1"></a><span class="op">&gt;&gt;&gt;</span> david <span class="op">=</span> Person(given_name<span class="op">=</span><span class="st">&#39;David&#39;</span>, family_name<span class="op">=</span><span class="st">&#39;Liu&#39;</span>, age<span class="op">=</span><span class="dv">100</span>, address<span class="op">=</span><span class="st">&#39;40 St. George Street&#39;</span>)</span></code></pre></div>
<p>Not only is this more explicit, but using keyword arguments allows us to pass the values in any order we want:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1"></a><span class="op">&gt;&gt;&gt;</span> david <span class="op">=</span> Person(family_name<span class="op">=</span><span class="st">&#39;Liu&#39;</span>, given_name<span class="op">=</span><span class="st">&#39;David&#39;</span>, address<span class="op">=</span><span class="st">&#39;40 St. George Street&#39;</span>, age<span class="op">=</span><span class="dv">100</span>)</span></code></pre></div>
<p>This is a great improvement for the readability of our code when we use data classes, especially as they grow larger. One potential downside that comes with this (and in general when being more explicit) is that this requires a bit more typing, and makes our code a little longer. You can get around the first issue by using auto-completion features (e.g., in PyCharm), and for the second issue you can put the different arguments on separate lines:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1"></a><span class="op">&gt;&gt;&gt;</span> david <span class="op">=</span> Person(</span>
<span id="cb10-2"><a href="#cb10-2"></a>... family_name<span class="op">=</span><span class="st">&#39;Liu&#39;</span>,</span>
<span id="cb10-3"><a href="#cb10-3"></a>... given_name<span class="op">=</span><span class="st">&#39;David&#39;</span>,</span>
<span id="cb10-4"><a href="#cb10-4"></a>... address<span class="op">=</span><span class="st">&#39;40 St. George Street&#39;</span>,</span>
<span id="cb10-5"><a href="#cb10-5"></a>... age<span class="op">=</span><span class="dv">100</span></span>
<span id="cb10-6"><a href="#cb10-6"></a>... )</span></code></pre></div>
<h3 id="representing-data-classes-in-the-memory-model">Representing data classes in the memory model</h3>
<p>Now that we have the ability to define our own data types, we need to decide how these data types will fit into our memory model. Well do this by using the representation that Python displays, formatted to show each instance attribute on a new line. For example, we would represent the <code>david</code> variable in a memory model as follows:</p>
<div class="memory-model-values" style="width:65%">
<table style="width:69%;">
<colgroup>
<col style="width: 15%" />
<col style="width: 54%" />
</colgroup>
<thead>
<tr class="header">
<th>Variable</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>david</code></td>
<td><div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1"></a>Person(</span>
<span id="cb11-2"><a href="#cb11-2"></a> family_name<span class="op">=</span><span class="st">&#39;Liu&#39;</span>,</span>
<span id="cb11-3"><a href="#cb11-3"></a> given_name<span class="op">=</span><span class="st">&#39;David&#39;</span>,</span>
<span id="cb11-4"><a href="#cb11-4"></a> address<span class="op">=</span><span class="st">&#39;40 St. George Street&#39;</span>,</span>
<span id="cb11-5"><a href="#cb11-5"></a> age<span class="op">=</span><span class="dv">100</span></span>
<span id="cb11-6"><a href="#cb11-6"></a>)</span></code></pre></div>
 </td>
</tr>
</tbody>
</table>
</div>
</section>
<!--
In Python, a **class** defines a data type.
The data types we've been working with so far (e.g., `int`, `dict`) are classes.
When we create, for example, a list, we have created an **object**.
Careful, an object and a class are two different things.
While a class defines the data type and you can think of it like a template or form.
An object is the template with all the blank spaces filled out.
We say that an **object** is an **instance** of a **class**.
-->
<footer>
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
</footer>
</body>
</html>
+458
View File
@@ -0,0 +1,458 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>4.3 Defining Our Own Data Types, Part 2</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="../tufte.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<div style="display:none">
\(
\newcommand{\NOT}{\neg}
\newcommand{\AND}{\wedge}
\newcommand{\OR}{\vee}
\newcommand{\XOR}{\oplus}
\newcommand{\IMP}{\Rightarrow}
\newcommand{\IFF}{\Leftrightarrow}
\newcommand{\TRUE}{\text{True}\xspace}
\newcommand{\FALSE}{\text{False}\xspace}
\newcommand{\IN}{\,{\in}\,}
\newcommand{\NOTIN}{\,{\notin}\,}
\newcommand{\TO}{\rightarrow}
\newcommand{\DIV}{\mid}
\newcommand{\NDIV}{\nmid}
\newcommand{\MOD}[1]{\pmod{#1}}
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
\newcommand{\N}{\mathbb N}
\newcommand{\Z}{\mathbb Z}
\newcommand{\Q}{\mathbb Q}
\newcommand{\R}{\mathbb R}
\newcommand{\C}{\mathbb C}
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
\newcommand{\cD}{\mathcal D}
\newcommand{\cE}{\mathcal E}
\newcommand{\cF}{\mathcal F}
\newcommand{\cG}{\mathcal G}
\newcommand{\cH}{\mathcal H}
\newcommand{\cI}{\mathcal I}
\newcommand{\cJ}{\mathcal J}
\newcommand{\cL}{\mathcal L}
\newcommand{\cK}{\mathcal K}
\newcommand{\cN}{\mathcal N}
\newcommand{\cO}{\mathcal O}
\newcommand{\cP}{\mathcal P}
\newcommand{\cQ}{\mathcal Q}
\newcommand{\cS}{\mathcal S}
\newcommand{\cT}{\mathcal T}
\newcommand{\cV}{\mathcal V}
\newcommand{\cW}{\mathcal W}
\newcommand{\cZ}{\mathcal Z}
\newcommand{\emp}{\emptyset}
\newcommand{\bs}{\backslash}
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
\newcommand{\abs}[1]{\left | #1 \right |}
\newcommand{\xspace}{}
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
\)
</div>
<header id="title-block-header">
<h1 class="title">4.3 Defining Our Own Data Types, Part 2</h1>
</header>
<section>
<p>In the previous section, we learned about <em>data classes</em>, a way to define our own data types in Python. In this section, were going to learn study some more details about defining and designing data classes in our programs, and apply what weve learned to simplify some of work we did with tabular data in <a href="01-tabular-data.html">4.1 Tabular Data</a>.</p>
<p>Before we begin, please take a moment to review the <code>Person</code> data class we developed in the previous section.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="im">from</span> dataclasses <span class="im">import</span> dataclass</span>
<span id="cb1-2"><a href="#cb1-2"></a></span>
<span id="cb1-3"><a href="#cb1-3"></a></span>
<span id="cb1-4"><a href="#cb1-4"></a><span class="at">@dataclass</span></span>
<span id="cb1-5"><a href="#cb1-5"></a><span class="kw">class</span> Person:</span>
<span id="cb1-6"><a href="#cb1-6"></a> <span class="co">&quot;&quot;&quot;A custom data type that represents data for a person.&quot;&quot;&quot;</span></span>
<span id="cb1-7"><a href="#cb1-7"></a> given_name: <span class="bu">str</span></span>
<span id="cb1-8"><a href="#cb1-8"></a> family_name: <span class="bu">str</span></span>
<span id="cb1-9"><a href="#cb1-9"></a> age: <span class="bu">int</span></span>
<span id="cb1-10"><a href="#cb1-10"></a> address: <span class="bu">str</span></span></code></pre></div>
<h2 id="constraining-data-class-values-representation-invariants">Constraining data class values: representation invariants</h2>
<p>In our <code>Person</code> data class definition, we specify the type of each instance attribute. By doing so, we constrain the possible values can be stored for these attributes. However, just as we saw with function type contracts, we dont always want to allow every possible value of a given type for an attribute value.</p>
<p>For example, the <code>age</code> attribute for <code>Person</code> has a type annotation <code>int</code>, but we certainly would not allow negative integers to be stored here! Somehow, wed like to record a second piece of information about this attribute: that <code>age &gt;= 0</code>. This kind of constraint is called a <strong>representation invariant</strong>, since it is a predicate describing a condition on how we <em>represent</em> a person that must always be true—this condition never varies.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> The term <em>invariant</em> is used in a few different contexts in computer science; well explore one other kind of invariant a bit later in this chapter.</span> All attribute type annotations, like <code>age: int</code>, are representation invariants. However, we can express general representation invariants as well, by adding them to the class docstring. Whenever possible, we write this as Python expressions rather than English, for a reason well see in the next section.</p>
<p>Here is how we add non-type-annotation representation invariants in a class docstring:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="at">@dataclass</span></span>
<span id="cb2-2"><a href="#cb2-2"></a><span class="kw">class</span> Person:</span>
<span id="cb2-3"><a href="#cb2-3"></a> <span class="co">&quot;&quot;&quot;A custom data type that represents data for a person.</span></span>
<span id="cb2-4"><a href="#cb2-4"></a></span>
<span id="cb2-5"><a href="#cb2-5"></a><span class="co"> Representation Invariants:</span></span>
<span id="cb2-6"><a href="#cb2-6"></a><span class="co"> - self.age &gt;= 0</span></span>
<span id="cb2-7"><a href="#cb2-7"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-8"><a href="#cb2-8"></a> given_name: <span class="bu">str</span></span>
<span id="cb2-9"><a href="#cb2-9"></a> family_name: <span class="bu">str</span></span>
<span id="cb2-10"><a href="#cb2-10"></a> age: <span class="bu">int</span></span>
<span id="cb2-11"><a href="#cb2-11"></a> address: <span class="bu">str</span></span></code></pre></div>
<p>One oddity with this definition is that we use <code>self.age</code> instead of <code>age</code> to refer to the instance attribute. This mimics how we access data type attributes using dot notation:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="op">&gt;&gt;&gt;</span> david <span class="op">=</span> Person(<span class="st">&#39;David&#39;</span>, <span class="st">&#39;Liu&#39;</span>, <span class="dv">100</span>, <span class="st">&#39;40 St. George Street&#39;</span>)</span>
<span id="cb3-2"><a href="#cb3-2"></a><span class="op">&gt;&gt;&gt;</span> david.age</span>
<span id="cb3-3"><a href="#cb3-3"></a><span class="dv">100</span></span></code></pre></div>
<p>In the class docstring, we use the variable name <code>self</code> to refer to a generic instance of the data class.<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote"> Keep in mind that <code>self</code> here is used just in the class docstring. In the above example, the variable <code>david</code> would appear in our memory model, but <code>self</code> would not.</span> This use of <code>self</code> is a strong Python convention, and well return to other uses of <code>self</code> later on in this course.</p>
<h3 id="checking-representation-invariants-automatically-with-python_ta">Checking representation invariants automatically with <code>python_ta</code></h3>
<p>Just as we saw with preconditions in <a href="../03-logic/07-function-specification.html">3.7 Function Specification</a>, representation invariants are useful pieces of documentation for how a data class should be used. Like preconditions, representation invariants are <em>assumptions</em> that we make about values of a data type; for example, we can assume that every <code>Person</code> instance has an <code>age</code> thats greater than or equal to zero.</p>
<p>Representation invariants are also <em>constraints</em> on how we can create a data class instance. Because it can be easy to miss or ignore a representation invariant buried in a class docstring, <code>python_ta.contracts</code> supposts checking all representation invariants, just like it does with preconditions! Lets add a <code>check_all_contracts</code> call to our <code>Person</code> example:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="im">from</span> dataclasses <span class="im">import</span> dataclass</span>
<span id="cb4-2"><a href="#cb4-2"></a></span>
<span id="cb4-3"><a href="#cb4-3"></a></span>
<span id="cb4-4"><a href="#cb4-4"></a><span class="at">@dataclass</span></span>
<span id="cb4-5"><a href="#cb4-5"></a><span class="kw">class</span> Person:</span>
<span id="cb4-6"><a href="#cb4-6"></a> <span class="co">&quot;&quot;&quot;A person with some basic demographic information.</span></span>
<span id="cb4-7"><a href="#cb4-7"></a></span>
<span id="cb4-8"><a href="#cb4-8"></a><span class="co"> Representation Invariants:</span></span>
<span id="cb4-9"><a href="#cb4-9"></a><span class="co"> - self.age &gt;= 0</span></span>
<span id="cb4-10"><a href="#cb4-10"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-11"><a href="#cb4-11"></a> given_name: <span class="bu">str</span></span>
<span id="cb4-12"><a href="#cb4-12"></a> family_name: <span class="bu">str</span></span>
<span id="cb4-13"><a href="#cb4-13"></a> age: <span class="bu">int</span></span>
<span id="cb4-14"><a href="#cb4-14"></a> address: <span class="bu">str</span></span>
<span id="cb4-15"><a href="#cb4-15"></a></span>
<span id="cb4-16"><a href="#cb4-16"></a></span>
<span id="cb4-17"><a href="#cb4-17"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&#39;__main__&#39;</span>:</span>
<span id="cb4-18"><a href="#cb4-18"></a> <span class="im">import</span> python_ta.contracts</span>
<span id="cb4-19"><a href="#cb4-19"></a> python_ta.contracts.DEBUG_CONTRACTS <span class="op">=</span> <span class="va">False</span></span>
<span id="cb4-20"><a href="#cb4-20"></a> python_ta.contracts.check_all_contracts()</span></code></pre></div>
<p>If we run the above file in the Python console, well obtain an error whenever we attempt to instantiate a <code>Person</code> with invalid attributes.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="op">&gt;&gt;&gt;</span> david <span class="op">=</span> Person(</span>
<span id="cb5-2"><a href="#cb5-2"></a>... given_name<span class="op">=</span><span class="st">&#39;David&#39;</span>,</span>
<span id="cb5-3"><a href="#cb5-3"></a>... family_name<span class="op">=</span><span class="st">&#39;Liu&#39;</span>,</span>
<span id="cb5-4"><a href="#cb5-4"></a>... age<span class="op">=-</span><span class="dv">100</span>,</span>
<span id="cb5-5"><a href="#cb5-5"></a>... address<span class="op">=</span><span class="st">&#39;40 St. George Street&#39;</span>)</span>
<span id="cb5-6"><a href="#cb5-6"></a>Traceback (most recent call last):</span>
<span id="cb5-7"><a href="#cb5-7"></a> File <span class="st">&quot;&lt;input&gt;&quot;</span>, line <span class="dv">1</span>, <span class="kw">in</span> <span class="op">&lt;</span>module<span class="op">&gt;</span></span>
<span id="cb5-8"><a href="#cb5-8"></a> ...</span>
<span id="cb5-9"><a href="#cb5-9"></a><span class="pp">AssertionError</span>: Representation invariant <span class="st">&quot;self.age &gt;= 0&quot;</span> violated.</span></code></pre></div>
<p><strong>Note</strong>: currently, <code>python_ta</code> is strict with the header <code>Representation Invariants:</code>. In particular, both the “<code>Representation</code>” and “<code>Invariants</code>” must be capitalized (and spelled correctly). Please watch out for this, as otherwise any representation invariants you add will not be checked!</p>
<h2 id="the-data-class-design-recipe">The data class design recipe</h2>
<p>Just as how functions give us a way of organizing blocks of code to represent a computation, data classes give us a way of organizing pieces of data to represent an entity. In <a href="../02-functions/05-the-function-design-recipe.html">2.5 The Function Design Recipe</a>, we learned a structured approach to designing and implementing functions. There is an analogous <strong>Data Class Design Recipe</strong>, which you should use every time you want to create a new data type for a program.<label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote"> Note the similarities between the two recipes, such as the importance of naming and documentation.</span></p>
<div class="fullwidth">
<table>
<colgroup>
<col style="width: 59%" />
<col style="width: 40%" />
</colgroup>
<tbody>
<tr class="odd">
<td><p><strong>1. Write the class header.</strong></p>
<p>The class header consists of three parts: the <code>@dataclass</code> decorator (dont forget to import from <code>dataclasses</code>), the keyword <code>class</code>, and the name of the data class. Pick a short noun or noun phrase as the name of the class. The name of the class should use the “CamelCase” naming convention: capitalize every word of the class name, and do <em>not</em> separate the words with underscores.</p></td>
<td><div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="at">@dataclass</span></span>
<span id="cb6-2"><a href="#cb6-2"></a><span class="kw">class</span> Person:</span></code></pre></div>
 </td>
</tr>
<tr class="even">
<td><p><strong>2. Write the instance attributes for the data class.</strong></p>
<p>Decide on what attributes you want the data class to bundle together. Remember that every instance of the data class will have <em>all</em> of these attributes.</p>
<p>Each attribute name should be a short noun or noun phrase, using “snake_case” (like function and variable names). Write each annotation name and its type indented within the data class body. |</p></td>
<td><div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="at">@dataclass</span></span>
<span id="cb7-2"><a href="#cb7-2"></a><span class="kw">class</span> Person:</span>
<span id="cb7-3"><a href="#cb7-3"></a> given_name: <span class="bu">str</span></span>
<span id="cb7-4"><a href="#cb7-4"></a> family_name: <span class="bu">str</span></span>
<span id="cb7-5"><a href="#cb7-5"></a> age: <span class="bu">int</span></span>
<span id="cb7-6"><a href="#cb7-6"></a> address: <span class="bu">str</span></span></code></pre></div>
 </td>
</tr>
<tr class="odd">
<td><p><strong>3. Write the data class docstring.</strong></p>
<p>Create a class docstring using triple-quotes, using the same format as function docstrings. Inside the docstring, write a description of the class and a description for every instance attribute. The class description should start with a one-line summary, and you can add a longer description underneath if necessary.</p>
<p>Use the header “Instance Attributes:” to mark the beginning of the attribute descriptions.</p></td>
<td><div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1"></a><span class="at">@dataclass</span></span>
<span id="cb8-2"><a href="#cb8-2"></a><span class="kw">class</span> Person:</span>
<span id="cb8-3"><a href="#cb8-3"></a> <span class="co">&quot;&quot;&quot;A data class representing a person.</span></span>
<span id="cb8-4"><a href="#cb8-4"></a></span>
<span id="cb8-5"><a href="#cb8-5"></a><span class="co"> Instance Attributes:</span></span>
<span id="cb8-6"><a href="#cb8-6"></a><span class="co"> - given_name: the person&#39;s given name</span></span>
<span id="cb8-7"><a href="#cb8-7"></a><span class="co"> - family_name: the person&#39;s family name</span></span>
<span id="cb8-8"><a href="#cb8-8"></a><span class="co"> - age: the person&#39;s age</span></span>
<span id="cb8-9"><a href="#cb8-9"></a><span class="co"> - address: the person&#39;s address</span></span>
<span id="cb8-10"><a href="#cb8-10"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb8-11"><a href="#cb8-11"></a> given_name: <span class="bu">str</span></span>
<span id="cb8-12"><a href="#cb8-12"></a> family_name: <span class="bu">str</span></span>
<span id="cb8-13"><a href="#cb8-13"></a> age: <span class="bu">int</span></span>
<span id="cb8-14"><a href="#cb8-14"></a> address: <span class="bu">str</span></span></code></pre></div>
 </td>
</tr>
<tr class="even">
<td><p><strong>4. Write an example instance (optional).</strong></p>
<p>At the bottom of the class docstring, write a doctest example of a typical instance of the data class. This should be used to illustrate all of the instance attributes, which is especially important when the instance attributes are complex types.</p></td>
<td><div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1"></a><span class="at">@dataclass</span></span>
<span id="cb9-2"><a href="#cb9-2"></a><span class="kw">class</span> Person:</span>
<span id="cb9-3"><a href="#cb9-3"></a> <span class="co">&quot;&quot;&quot;A data class representing a person.</span></span>
<span id="cb9-4"><a href="#cb9-4"></a></span>
<span id="cb9-5"><a href="#cb9-5"></a><span class="co"> Instance Attributes:</span></span>
<span id="cb9-6"><a href="#cb9-6"></a><span class="co"> - given_name: the person&#39;s given name</span></span>
<span id="cb9-7"><a href="#cb9-7"></a><span class="co"> - family_name: the person&#39;s family name</span></span>
<span id="cb9-8"><a href="#cb9-8"></a><span class="co"> - age: the person&#39;s age</span></span>
<span id="cb9-9"><a href="#cb9-9"></a><span class="co"> - address: the person&#39;s address</span></span>
<span id="cb9-10"><a href="#cb9-10"></a></span>
<span id="cb9-11"><a href="#cb9-11"></a><span class="co"> &gt;&gt;&gt; david = Person(</span></span>
<span id="cb9-12"><a href="#cb9-12"></a><span class="co"> ... &#39;David&#39;,</span></span>
<span id="cb9-13"><a href="#cb9-13"></a><span class="co"> ... &#39;Liu&#39;,</span></span>
<span id="cb9-14"><a href="#cb9-14"></a><span class="co"> ... 40,</span></span>
<span id="cb9-15"><a href="#cb9-15"></a><span class="co"> ... &#39;40 St. George Street&#39;</span></span>
<span id="cb9-16"><a href="#cb9-16"></a><span class="co"> ... )</span></span>
<span id="cb9-17"><a href="#cb9-17"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb9-18"><a href="#cb9-18"></a> given_name: <span class="bu">str</span></span>
<span id="cb9-19"><a href="#cb9-19"></a> family_name: <span class="bu">str</span></span>
<span id="cb9-20"><a href="#cb9-20"></a> age: <span class="bu">int</span></span>
<span id="cb9-21"><a href="#cb9-21"></a> address: <span class="bu">str</span></span></code></pre></div>
 </td>
</tr>
<tr class="odd">
<td><p><strong>5. Document any additional representation invariants.</strong></p>
<p>If there are representation invariants for the instance attributes beyond the type annotations, include them in the class docstring under a separate section “Representation Invariants:” in between the instance attribute descriptions and sample instance.</p>
<p>Just as with function preconditions, each representation invariant should be a boolean expression in Python. Use <code>self.&lt;attribute&gt;</code> to refer to an instance attribute within a representation invariant.</p></td>
<td><div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1"></a><span class="at">@dataclass</span></span>
<span id="cb10-2"><a href="#cb10-2"></a><span class="kw">class</span> Person:</span>
<span id="cb10-3"><a href="#cb10-3"></a> <span class="co">&quot;&quot;&quot;A data class representing a person.</span></span>
<span id="cb10-4"><a href="#cb10-4"></a></span>
<span id="cb10-5"><a href="#cb10-5"></a><span class="co"> Instance Attributes:</span></span>
<span id="cb10-6"><a href="#cb10-6"></a><span class="co"> - given_name: the person&#39;s given name</span></span>
<span id="cb10-7"><a href="#cb10-7"></a><span class="co"> - family_name: the person&#39;s family name</span></span>
<span id="cb10-8"><a href="#cb10-8"></a><span class="co"> - age: the person&#39;s age</span></span>
<span id="cb10-9"><a href="#cb10-9"></a><span class="co"> - address: the person&#39;s address</span></span>
<span id="cb10-10"><a href="#cb10-10"></a></span>
<span id="cb10-11"><a href="#cb10-11"></a><span class="co"> Representation Invariants:</span></span>
<span id="cb10-12"><a href="#cb10-12"></a><span class="co"> - self.age &gt;= 0</span></span>
<span id="cb10-13"><a href="#cb10-13"></a></span>
<span id="cb10-14"><a href="#cb10-14"></a><span class="co"> &gt;&gt;&gt; david = Person(</span></span>
<span id="cb10-15"><a href="#cb10-15"></a><span class="co"> ... &#39;David&#39;,</span></span>
<span id="cb10-16"><a href="#cb10-16"></a><span class="co"> ... &#39;Liu&#39;,</span></span>
<span id="cb10-17"><a href="#cb10-17"></a><span class="co"> ... 40,</span></span>
<span id="cb10-18"><a href="#cb10-18"></a><span class="co"> ... &#39;40 St. George Street&#39;</span></span>
<span id="cb10-19"><a href="#cb10-19"></a><span class="co"> ... )</span></span>
<span id="cb10-20"><a href="#cb10-20"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb10-21"><a href="#cb10-21"></a> given_name: <span class="bu">str</span></span>
<span id="cb10-22"><a href="#cb10-22"></a> family_name: <span class="bu">str</span></span>
<span id="cb10-23"><a href="#cb10-23"></a> age: <span class="bu">int</span></span>
<span id="cb10-24"><a href="#cb10-24"></a> address: <span class="bu">str</span></span></code></pre></div>
 </td>
</tr>
</tbody>
</table>
</div>
<h2 id="a-worked-example">A worked example</h2>
<p>To wrap up our introduction of data classes, lets see how to apply data classes to the marriage license data set we studied in <a href="01-tabular-data.html">4.1 Tabular Data</a>.</p>
<table>
<thead>
<tr class="header">
<th style="text-align: center;"><strong>ID</strong></th>
<th style="text-align: center;"><strong>Civic Centre</strong></th>
<th style="text-align: center;"><strong>Marriage Licenses Issued</strong></th>
<th style="text-align: center;"><strong>Time Period</strong></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: center;">1657</td>
<td style="text-align: center;">ET</td>
<td style="text-align: center;">80</td>
<td style="text-align: center;">January 1, 2011</td>
</tr>
<tr class="even">
<td style="text-align: center;">1658</td>
<td style="text-align: center;">NY</td>
<td style="text-align: center;">136</td>
<td style="text-align: center;">January 1, 2011</td>
</tr>
<tr class="odd">
<td style="text-align: center;">1659</td>
<td style="text-align: center;">SC</td>
<td style="text-align: center;">159</td>
<td style="text-align: center;">January 1, 2011</td>
</tr>
<tr class="even">
<td style="text-align: center;">1660</td>
<td style="text-align: center;">TO</td>
<td style="text-align: center;">367</td>
<td style="text-align: center;">January 1, 2011</td>
</tr>
<tr class="odd">
<td style="text-align: center;">1661</td>
<td style="text-align: center;">ET</td>
<td style="text-align: center;">109</td>
<td style="text-align: center;">February 1, 2011</td>
</tr>
<tr class="even">
<td style="text-align: center;">1662</td>
<td style="text-align: center;">NY</td>
<td style="text-align: center;">150</td>
<td style="text-align: center;">February 1, 2011</td>
</tr>
<tr class="odd">
<td style="text-align: center;">1663</td>
<td style="text-align: center;">SC</td>
<td style="text-align: center;">154</td>
<td style="text-align: center;">February 1, 2011</td>
</tr>
<tr class="even">
<td style="text-align: center;">1664</td>
<td style="text-align: center;">TO</td>
<td style="text-align: center;">383</td>
<td style="text-align: center;">February 1, 2011</td>
</tr>
</tbody>
</table>
<p>Recall that we represented the data as a list of lists:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1"></a><span class="op">&gt;&gt;&gt;</span> marriage_data <span class="op">=</span> [</span>
<span id="cb11-2"><a href="#cb11-2"></a>... [<span class="dv">1657</span>, <span class="st">&#39;ET&#39;</span>, <span class="dv">80</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)],</span>
<span id="cb11-3"><a href="#cb11-3"></a>... [<span class="dv">1658</span>, <span class="st">&#39;NY&#39;</span>, <span class="dv">136</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)],</span>
<span id="cb11-4"><a href="#cb11-4"></a>... [<span class="dv">1659</span>, <span class="st">&#39;SC&#39;</span>, <span class="dv">159</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)],</span>
<span id="cb11-5"><a href="#cb11-5"></a>... [<span class="dv">1660</span>, <span class="st">&#39;TO&#39;</span>, <span class="dv">367</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)],</span>
<span id="cb11-6"><a href="#cb11-6"></a>... [<span class="dv">1661</span>, <span class="st">&#39;ET&#39;</span>, <span class="dv">109</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)],</span>
<span id="cb11-7"><a href="#cb11-7"></a>... [<span class="dv">1662</span>, <span class="st">&#39;NY&#39;</span>, <span class="dv">150</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)],</span>
<span id="cb11-8"><a href="#cb11-8"></a>... [<span class="dv">1663</span>, <span class="st">&#39;SC&#39;</span>, <span class="dv">154</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)],</span>
<span id="cb11-9"><a href="#cb11-9"></a>... [<span class="dv">1664</span>, <span class="st">&#39;TO&#39;</span>, <span class="dv">383</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)]</span>
<span id="cb11-10"><a href="#cb11-10"></a>... ]</span></code></pre></div>
<p>We implemented the following function to calculate the average number of marriage licenses issued by a particular civic centre:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1"></a><span class="kw">def</span> average_licenses_issued(data: <span class="bu">list</span>[<span class="bu">list</span>], civic_centre: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">float</span>:</span>
<span id="cb12-2"><a href="#cb12-2"></a> <span class="co">&quot;&quot;&quot;Return the average number of marriage licenses issued by civic_centre in data.</span></span>
<span id="cb12-3"><a href="#cb12-3"></a></span>
<span id="cb12-4"><a href="#cb12-4"></a><span class="co"> Precondition:</span></span>
<span id="cb12-5"><a href="#cb12-5"></a><span class="co"> - all({len(row) == 4 for row in data})</span></span>
<span id="cb12-6"><a href="#cb12-6"></a><span class="co"> - any({row[1] == civic_centre for row in data})</span></span>
<span id="cb12-7"><a href="#cb12-7"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb12-8"><a href="#cb12-8"></a> issued_by_civic_centre <span class="op">=</span> [row[<span class="dv">2</span>] <span class="cf">for</span> row <span class="kw">in</span> data <span class="cf">if</span> row[<span class="dv">1</span>] <span class="op">==</span> civic_centre]</span>
<span id="cb12-9"><a href="#cb12-9"></a></span>
<span id="cb12-10"><a href="#cb12-10"></a> total <span class="op">=</span> <span class="bu">sum</span>(issued_by_civic_centre)</span>
<span id="cb12-11"><a href="#cb12-11"></a> count <span class="op">=</span> <span class="bu">len</span>(issued_by_civic_centre)</span>
<span id="cb12-12"><a href="#cb12-12"></a></span>
<span id="cb12-13"><a href="#cb12-13"></a> <span class="cf">return</span> total <span class="op">/</span> count</span></code></pre></div>
<p>Here is how we will use data classes to simplify this approach. Rather than storing each row in the table as a list, we can instead introduce a new data class to store this information:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1"></a><span class="im">from</span> dataclasses <span class="im">import</span> dataclass</span>
<span id="cb13-2"><a href="#cb13-2"></a><span class="im">from</span> datetime <span class="im">import</span> date</span>
<span id="cb13-3"><a href="#cb13-3"></a></span>
<span id="cb13-4"><a href="#cb13-4"></a></span>
<span id="cb13-5"><a href="#cb13-5"></a><span class="at">@dataclass</span></span>
<span id="cb13-6"><a href="#cb13-6"></a><span class="kw">class</span> MarriageData:</span>
<span id="cb13-7"><a href="#cb13-7"></a> <span class="co">&quot;&quot;&quot;A record of the number of marriage licenses issued in a civic centre in a given month.</span></span>
<span id="cb13-8"><a href="#cb13-8"></a></span>
<span id="cb13-9"><a href="#cb13-9"></a><span class="co"> Instance Attributes:</span></span>
<span id="cb13-10"><a href="#cb13-10"></a><span class="co"> - id: a unique identifier for the record</span></span>
<span id="cb13-11"><a href="#cb13-11"></a><span class="co"> - civic_centre: the name of the civic centre</span></span>
<span id="cb13-12"><a href="#cb13-12"></a><span class="co"> - num_licenses: the number of licenses issued</span></span>
<span id="cb13-13"><a href="#cb13-13"></a><span class="co"> - month: the month these licenses were issued</span></span>
<span id="cb13-14"><a href="#cb13-14"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb13-15"><a href="#cb13-15"></a> <span class="bu">id</span>: <span class="bu">int</span></span>
<span id="cb13-16"><a href="#cb13-16"></a> civic_centre: <span class="bu">str</span></span>
<span id="cb13-17"><a href="#cb13-17"></a> num_licenses: <span class="bu">int</span></span>
<span id="cb13-18"><a href="#cb13-18"></a> month: date</span></code></pre></div>
<p>Then using this data class, we can represent tabular data as a list of <code>MarriageData</code> instances rather than a list of lists. Not much has changed! The values representing each entry in the table are the same, but how we “bundle” each row of data into a single entity is different.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1"></a><span class="op">&gt;&gt;&gt;</span> marriage_data <span class="op">=</span> [</span>
<span id="cb14-2"><a href="#cb14-2"></a>... MarriageData(<span class="dv">1657</span>, <span class="st">&#39;ET&#39;</span>, <span class="dv">80</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)),</span>
<span id="cb14-3"><a href="#cb14-3"></a>... MarriageData(<span class="dv">1658</span>, <span class="st">&#39;NY&#39;</span>, <span class="dv">136</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)),</span>
<span id="cb14-4"><a href="#cb14-4"></a>... MarriageData(<span class="dv">1659</span>, <span class="st">&#39;SC&#39;</span>, <span class="dv">159</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)),</span>
<span id="cb14-5"><a href="#cb14-5"></a>... MarriageData(<span class="dv">1660</span>, <span class="st">&#39;TO&#39;</span>, <span class="dv">367</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">1</span>, <span class="dv">1</span>)),</span>
<span id="cb14-6"><a href="#cb14-6"></a>... MarriageData(<span class="dv">1661</span>, <span class="st">&#39;ET&#39;</span>, <span class="dv">109</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)),</span>
<span id="cb14-7"><a href="#cb14-7"></a>... MarriageData(<span class="dv">1662</span>, <span class="st">&#39;NY&#39;</span>, <span class="dv">150</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)),</span>
<span id="cb14-8"><a href="#cb14-8"></a>... MarriageData(<span class="dv">1663</span>, <span class="st">&#39;SC&#39;</span>, <span class="dv">154</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>)),</span>
<span id="cb14-9"><a href="#cb14-9"></a>... MarriageData(<span class="dv">1664</span>, <span class="st">&#39;TO&#39;</span>, <span class="dv">383</span>, datetime.date(<span class="dv">2011</span>, <span class="dv">2</span>, <span class="dv">1</span>))</span>
<span id="cb14-10"><a href="#cb14-10"></a>... ]</span></code></pre></div>
<p>And here is how we could modify our <code>average_licenses_issued</code> function.</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1"></a><span class="kw">def</span> average_licenses_issued(data: <span class="bu">list</span>[MarriageData], civic_centre: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">float</span>:</span>
<span id="cb15-2"><a href="#cb15-2"></a> <span class="co">&quot;&quot;&quot;Return the average number of marriage licenses issued by civic_centre in data.</span></span>
<span id="cb15-3"><a href="#cb15-3"></a></span>
<span id="cb15-4"><a href="#cb15-4"></a><span class="co"> Precondition:</span></span>
<span id="cb15-5"><a href="#cb15-5"></a><span class="co"> - any({row.civic_centre == civic_centre for row in data})</span></span>
<span id="cb15-6"><a href="#cb15-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb15-7"><a href="#cb15-7"></a> issued_by_civic_centre <span class="op">=</span> [</span>
<span id="cb15-8"><a href="#cb15-8"></a> row.num_licenses <span class="cf">for</span> row <span class="kw">in</span> data <span class="cf">if</span> row.civic_centre <span class="op">==</span> civic_centre</span>
<span id="cb15-9"><a href="#cb15-9"></a> ]</span>
<span id="cb15-10"><a href="#cb15-10"></a></span>
<span id="cb15-11"><a href="#cb15-11"></a> total <span class="op">=</span> <span class="bu">sum</span>(issued_by_civic_centre)</span>
<span id="cb15-12"><a href="#cb15-12"></a> count <span class="op">=</span> <span class="bu">len</span>(issued_by_civic_centre)</span>
<span id="cb15-13"><a href="#cb15-13"></a></span>
<span id="cb15-14"><a href="#cb15-14"></a> <span class="cf">return</span> total <span class="op">/</span> count</span></code></pre></div>
<p>Again, not much has changed: instead of writing <code>row[1]</code> and <code>row[2]</code>, we instead write <code>row.civic_centre</code> and <code>row.num_licenses</code>. This is longer to write, but also more explicit in what attributes of the data are accessed. And to quote from the <a href="https://www.python.org/dev/peps/pep-0020/">Zen of Python</a>, <em>explicit is better than implicit</em>.</p>
<h2 id="summary-why-data-classes">Summary: why data classes?</h2>
<p>Earlier, we claimed that a <code>dataclass</code> is a better way of representing a bundle of data than a list. Lets review a few reasons why:</p>
<ol type="1">
<li>We now access the different attributes by name rather than index in the list, which is easier to remember and understand if youre reading the code.</li>
<li>Similarly, software like PyCharm and <code>python_ta</code> understand data class definitions, and will warn us if we try to create <em>malformed person values</em> (e.g., wrong arguments to <code>Person</code>), or access invalid attributes.</li>
<li>Lists are designed to be a very flexible and general data type, and support many operations (e.g. list concatenation and “element of”) that we dont want to do for actual people or rows of marriage data. Now that we use a separate data class, we eliminate the possibility of using these list operations on a “marriage data row”, even accidentally.</li>
</ol>
</section>
<footer>
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
</footer>
</body>
</html>
+497
View File
@@ -0,0 +1,497 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>4.4 Repeated Execution: For Loops</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="../tufte.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<div style="display:none">
\(
\newcommand{\NOT}{\neg}
\newcommand{\AND}{\wedge}
\newcommand{\OR}{\vee}
\newcommand{\XOR}{\oplus}
\newcommand{\IMP}{\Rightarrow}
\newcommand{\IFF}{\Leftrightarrow}
\newcommand{\TRUE}{\text{True}\xspace}
\newcommand{\FALSE}{\text{False}\xspace}
\newcommand{\IN}{\,{\in}\,}
\newcommand{\NOTIN}{\,{\notin}\,}
\newcommand{\TO}{\rightarrow}
\newcommand{\DIV}{\mid}
\newcommand{\NDIV}{\nmid}
\newcommand{\MOD}[1]{\pmod{#1}}
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
\newcommand{\N}{\mathbb N}
\newcommand{\Z}{\mathbb Z}
\newcommand{\Q}{\mathbb Q}
\newcommand{\R}{\mathbb R}
\newcommand{\C}{\mathbb C}
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
\newcommand{\cD}{\mathcal D}
\newcommand{\cE}{\mathcal E}
\newcommand{\cF}{\mathcal F}
\newcommand{\cG}{\mathcal G}
\newcommand{\cH}{\mathcal H}
\newcommand{\cI}{\mathcal I}
\newcommand{\cJ}{\mathcal J}
\newcommand{\cL}{\mathcal L}
\newcommand{\cK}{\mathcal K}
\newcommand{\cN}{\mathcal N}
\newcommand{\cO}{\mathcal O}
\newcommand{\cP}{\mathcal P}
\newcommand{\cQ}{\mathcal Q}
\newcommand{\cS}{\mathcal S}
\newcommand{\cT}{\mathcal T}
\newcommand{\cV}{\mathcal V}
\newcommand{\cW}{\mathcal W}
\newcommand{\cZ}{\mathcal Z}
\newcommand{\emp}{\emptyset}
\newcommand{\bs}{\backslash}
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
\newcommand{\abs}[1]{\left | #1 \right |}
\newcommand{\xspace}{}
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
\)
</div>
<header id="title-block-header">
<h1 class="title">4.4 Repeated Execution: For Loops</h1>
</header>
<section>
<p>Collections in Python can be used in many ways. We have already seen how we can use built-in aggregation functions (e.g., <code>any</code>, <code>all</code>, <code>max</code>) to perform computations across all elements of a collection (e.g., <code>list</code>, <code>set</code>).</p>
<p>But right now, were limited by what aggregation functions Python makes available to us: for example, theres a built-in <code>sum</code> function, but no <code>product</code> function.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> Thats not exactly true: there is a <code>math.product</code> function, but lets ignore that here. :)</span> So in this section, well learn about the <code>for</code> loop, a compound statement that will allow us to implement our own custom aggregation functions across different types of collection data.</p>
<h2 id="introducing-the-problem-repeating-code">Introducing the problem: repeating code</h2>
<p>Suppose we wanted to write a function that computes the sum of a list of numbers, without using the built-in <code>sum</code> function.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">def</span> my_sum(numbers: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb1-2"><a href="#cb1-2"></a> <span class="co">&quot;&quot;&quot;Return the sum of the given numbers.</span></span>
<span id="cb1-3"><a href="#cb1-3"></a></span>
<span id="cb1-4"><a href="#cb1-4"></a><span class="co"> &gt;&gt;&gt; my_sum([10, 20, 30])</span></span>
<span id="cb1-5"><a href="#cb1-5"></a><span class="co"> 60</span></span>
<span id="cb1-6"><a href="#cb1-6"></a><span class="co"> &quot;&quot;&quot;</span></span></code></pre></div>
<p>If we knew the size of <code>numbers</code> in advance, we could write a single expression to do this. For example, here is how we could implement <code>my_sum</code> if we knew that <code>numbers</code> always contained three elements:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">def</span> my_sum(numbers: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb2-2"><a href="#cb2-2"></a> <span class="co">&quot;&quot;&quot;Return the sum of the given numbers.</span></span>
<span id="cb2-3"><a href="#cb2-3"></a></span>
<span id="cb2-4"><a href="#cb2-4"></a><span class="co"> &gt;&gt;&gt; my_sum([10, 20, 30])</span></span>
<span id="cb2-5"><a href="#cb2-5"></a><span class="co"> 60</span></span>
<span id="cb2-6"><a href="#cb2-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-7"><a href="#cb2-7"></a> <span class="cf">return</span> numbers[<span class="dv">0</span>] <span class="op">+</span> numbers[<span class="dv">1</span>] <span class="op">+</span> numbers[<span class="dv">2</span>]</span></code></pre></div>
<p>But of course, this approach doesnt work for general lists, when we dont know ahead of time how many elements the input will have. We need a way to repeat the “<code>+ numbers[_]</code>” for an arbitrary number of list elements. Here is another way of writing our three-element code to pull out the exact statement that is repeated.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">def</span> my_sum(numbers: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb3-2"><a href="#cb3-2"></a> <span class="co">&quot;&quot;&quot;Return the sum of the given numbers.</span></span>
<span id="cb3-3"><a href="#cb3-3"></a></span>
<span id="cb3-4"><a href="#cb3-4"></a><span class="co"> &gt;&gt;&gt; my_sum([10, 20, 30])</span></span>
<span id="cb3-5"><a href="#cb3-5"></a><span class="co"> 60</span></span>
<span id="cb3-6"><a href="#cb3-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb3-7"><a href="#cb3-7"></a> sum_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb3-8"><a href="#cb3-8"></a></span>
<span id="cb3-9"><a href="#cb3-9"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> numbers[<span class="dv">0</span>]</span>
<span id="cb3-10"><a href="#cb3-10"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> numbers[<span class="dv">1</span>]</span>
<span id="cb3-11"><a href="#cb3-11"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> numbers[<span class="dv">2</span>]</span>
<span id="cb3-12"><a href="#cb3-12"></a></span>
<span id="cb3-13"><a href="#cb3-13"></a> <span class="cf">return</span> sum_so_far</span></code></pre></div>
<p>This implementation follows how a human might add up the numbers in the list. First, we start a counter a 0 (using a variable called <code>sum_so_far</code>). Then, we use three assignment statements to update the value of <code>sum_so_far</code> by adding another element of <code>numbers</code>. Lets look at the first such statement:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a>sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> numbers[<span class="dv">0</span>]</span></code></pre></div>
<p>This looks fairly straightforward, but is actually a big leap from the assignment statements weve studied before! Whats unusual about it is that for the first time, we are assigning a value to a variable that has <em>already</em> been given a value. This type of assignment statement is called a <strong>variable reassignment statement</strong>. This statement is especially tricky because the variable <code>sum_so_far</code> appears on both sides of the <code>=</code>. We can make sense of this statement by reviewing the evaluation order that Python follows when executing an assignment statement:</p>
<ul>
<li>First, the right-hand side of the assignment statement (<code>sum_so_far + numbers[0]</code>) is evaluated.</li>
<li>Second, the value produced by evaluating the right-hand side is stored in the variable on the left-hand side (<code>sum_so_far</code>).</li>
</ul>
<p>We can visualize how the three assignment statements work by tracing through an example. Lets consider calling our doctest example, <code>my_sum([10, 20, 30])</code>. What happens to the value of <code>sum_so_far</code>?</p>
<div class="fullwidth reference-table">
<table>
<colgroup>
<col style="width: 39%" />
<col style="width: 22%" />
<col style="width: 38%" />
</colgroup>
<thead>
<tr class="header">
<th>Statement</th>
<th><code>sum_so_far</code> <em>after</em> executing statement</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>sum_so_far = 0</code></td>
<td><code>0</code></td>
<td></td>
</tr>
<tr class="even">
<td><code>sum_so_far = sum_so_far + numbers[0]</code></td>
<td><code>10</code> (<code>0 + 10</code>)</td>
<td>When evaluating the right-hand side, <code>sum_so_far</code> is <code>0</code> and <code>numbers[0]</code> is <code>10</code>.</td>
</tr>
<tr class="odd">
<td><code>sum_so_far = sum_so_far + numbers[1]</code></td>
<td><code>30</code> (<code>10 + 20</code>)</td>
<td>When evaluating the right-hand side, <code>sum_so_far</code> is <code>10</code> and <code>numbers[1]</code> is <code>20</code>.</td>
</tr>
<tr class="even">
<td><code>sum_so_far = sum_so_far + numbers[2]</code></td>
<td><code>60</code> (<code>30 + 30</code>)</td>
<td>When evaluating the right-hand side, <code>sum_so_far</code> is <code>30</code> and <code>numbers[2]</code> is <code>30</code>.</td>
</tr>
</tbody>
</table>
</div>
<p>Now that we understand this implementation, we can see that the statement <code>sum_so_far = sum_so_far + numbers[_]</code> is exactly what needs to be repeated for every element of the input list. So now, lets learn how to perform <em>repeated execution</em> of Python statements.</p>
<h2 id="the-for-loop">The for loop</h2>
<p>In Python, the <strong>for loop</strong> is a compound statement that repeats a block of code once for element in a collection. Here is the syntax of a for loop:<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote"> Notice that the syntax is very similar to a comprehension. The key difference is that a comprehension evaluates an <em>expression</em> once for each element in a collection, but a for loop evaluates a <em>sequence of statements</em> once per element.</span></p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="cf">for</span> <span class="op">&lt;</span>loop_variable<span class="op">&gt;</span> <span class="kw">in</span> <span class="op">&lt;</span>collection<span class="op">&gt;</span>:</span>
<span id="cb5-2"><a href="#cb5-2"></a> <span class="op">&lt;</span>body<span class="op">&gt;</span></span></code></pre></div>
<p>There are three parts:</p>
<ol type="1">
<li><p><code>&lt;collection&gt;</code> is an expression for a Python collection (e.g., a <code>list</code> or <code>set</code>).</p></li>
<li><p><code>&lt;loop_variable&gt;</code> is a name for the <em>loop variable</em> that will refer to an element in the colleciton.</p></li>
<li><p><code>&lt;body&gt;</code> is a sequence of one or more statements that will be repeatedly executed. This is called the <em>body</em> of the for loop. The statements within the loop body may refer to the loop variable to access the “current” element in the collection.</p>
<p>Just as we saw with if statements, the body of a for loop <em>must</em> be indented relative to the <code>for</code> keyword.</p></li>
</ol>
<p>When a for loop is executed, the following happens:</p>
<ol type="1">
<li><p>The loop variable is assigned to the first element in the collection.</p></li>
<li><p>The loop body is executed, using the current value of the loop variable.</p></li>
<li><p>Steps 1 and 2 repeat for the second element of the collection, then the third, etc. until all elements of the collection have been assigned to the loop variable exactly once.</p>
<p>Each individual execution of the loop body is called a <strong>loop iteration</strong>.</p></li>
</ol>
<p>As with if statements, for loops are a control flow structure in Python because they modify the order in which statements are executed—in this case, by repeating a block of code multiple times. The reason we use the term <em>loop</em> is because after the last statement in the loop body is executed, the Python interpreter “loops back” to the beginning of the for loop, assigning the loop variable to the next element in the collection.</p>
<!-- TODO: Add general control flow diagram for for loops -->
<h2 id="my_sum-and-the-accumulator-pattern"><code>my_sum</code> and the accumulator pattern</h2>
<p>Now let us see how to use a for loop to implement <code>my_sum</code>. We left off with the following block of repeated code:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> numbers[<span class="dv">0</span>]</span>
<span id="cb6-2"><a href="#cb6-2"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> numbers[<span class="dv">1</span>]</span>
<span id="cb6-3"><a href="#cb6-3"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> numbers[<span class="dv">2</span>]</span></code></pre></div>
<p>We can now move the repeated <code>sum_so_far = sum_so_far + _</code> part into a <code>for</code> loop as follows:<label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote"> Notice our loop variable name! A good convention to follow is that collections have a pluralized name (<code>numbers</code>), and loop variables have the singular version of that name (<code>number</code>).</span></p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="cf">for</span> number <span class="kw">in</span> numbers:</span>
<span id="cb7-2"><a href="#cb7-2"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> number</span></code></pre></div>
<p>One important thing to note is that we no longer need to use list indexing (<code>numbers[_]</code>) to access individual list elements. The for loop in Python handles the extracing of individual elements for us, so that our loop body can focus just on what to do with each element.</p>
<p>With this, we can now write our complete implementation of <code>my_sum</code>.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1"></a><span class="kw">def</span> my_sum(numbers: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb8-2"><a href="#cb8-2"></a> <span class="co">&quot;&quot;&quot;Return the sum of the given numbers.</span></span>
<span id="cb8-3"><a href="#cb8-3"></a></span>
<span id="cb8-4"><a href="#cb8-4"></a><span class="co"> &gt;&gt;&gt; my_sum([10, 20, 30])</span></span>
<span id="cb8-5"><a href="#cb8-5"></a><span class="co"> 60</span></span>
<span id="cb8-6"><a href="#cb8-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb8-7"><a href="#cb8-7"></a> sum_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb8-8"><a href="#cb8-8"></a></span>
<span id="cb8-9"><a href="#cb8-9"></a> <span class="cf">for</span> number <span class="kw">in</span> numbers:</span>
<span id="cb8-10"><a href="#cb8-10"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> number</span>
<span id="cb8-11"><a href="#cb8-11"></a></span>
<span id="cb8-12"><a href="#cb8-12"></a> <span class="cf">return</span> sum_so_far</span></code></pre></div>
<p>Now, no matter how many elements <code>numbers</code> has, the loop body <code>sum_so_far = sum_so_far + number</code> will repeat once for each element. The ability to write a small amount of code that processes an arbitrary amount of data is one of the truly remarkable feats of computer science.</p>
<h3 id="accumulators-and-tracing-through-loops">Accumulators and tracing through loops</h3>
<p>Because of the variable reassignment, <code>sum_so_far</code> is more complex than every other variable we have used so far in this course. And because this reassignment happens inside the loop body, it happens once for each element in the collection, not just once or twice. This frequent reassignment can make loops hard to reason about, especially as our loop bodies grow more complex, and so we will take some time now to introduce a formal process you can use to reason about loops in your code.</p>
<p>First, some terminology. We call the variable <code>sum_so_far</code> the <strong>loop accumulator</strong>. The purpose of a loop accumulator is to store an aggregated result based on the elements of the collection that have been previously visited by the loop. In the case of <code>my_sum</code>, the loop accumulator <code>sum_so_far</code> stores, well, the sum of the elements that we have seen so far in the loop. We can keep track of the execution of the different iterations of the loop in a tracing table consisting of three columms: how many iterations have occurred so far, the value of the loop variable for that iteration, and the value of the loop accumulator at the <em>end</em> of that iteration. We call this table a <strong>loop accumulation table</strong>. Here is the loop accumulation table for a call to <code>my_sum([10, 20, 30])</code>:</p>
<div class="reference-table">
<table>
<thead>
<tr class="header">
<th>Iteration</th>
<th>Loop variable (<code>number</code>)</th>
<th>Loop accumulator (<code>sum_so_far</code>)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>0</td>
<td>N/A</td>
<td>0</td>
</tr>
<tr class="even">
<td>1</td>
<td>10</td>
<td>10</td>
</tr>
<tr class="odd">
<td>2</td>
<td>20</td>
<td>30</td>
</tr>
<tr class="even">
<td>3</td>
<td>30</td>
<td>60</td>
</tr>
</tbody>
</table>
</div>
<p>Almost every for loop has an accumulator variable.<label for="sn-3" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-3" class="margin-toggle"/><span class="sidenote"> Later, some might even have more than one.</span> To distinguish these from other variables, we recommend using the <code>_so_far</code> suffix in the variable name, and optionally adding a comment in your code explaining the purpose of the variable.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1"></a><span class="kw">def</span> my_sum(numbers: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb9-2"><a href="#cb9-2"></a> <span class="co">&quot;&quot;&quot;Return the sum of the numbers in numbers.</span></span>
<span id="cb9-3"><a href="#cb9-3"></a></span>
<span id="cb9-4"><a href="#cb9-4"></a><span class="co"> &gt;&gt;&gt; my_sum([10, 20, 30])</span></span>
<span id="cb9-5"><a href="#cb9-5"></a><span class="co"> 60</span></span>
<span id="cb9-6"><a href="#cb9-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb9-7"><a href="#cb9-7"></a> <span class="co"># ACCUMULATOR sum_so_far: keep track of the running sum of the elements in numbers.</span></span>
<span id="cb9-8"><a href="#cb9-8"></a> sum_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb9-9"><a href="#cb9-9"></a></span>
<span id="cb9-10"><a href="#cb9-10"></a> <span class="cf">for</span> number <span class="kw">in</span> numbers:</span>
<span id="cb9-11"><a href="#cb9-11"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> number</span>
<span id="cb9-12"><a href="#cb9-12"></a></span>
<span id="cb9-13"><a href="#cb9-13"></a> <span class="cf">return</span> sum_so_far</span></code></pre></div>
<h3 id="when-the-collection-is-empty">When the collection is empty</h3>
<p>What happens if we call <code>my_sum</code> on an empty list?</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1"></a><span class="op">&gt;&gt;&gt;</span> my_sum([])</span>
<span id="cb10-2"><a href="#cb10-2"></a><span class="dv">0</span></span></code></pre></div>
<p>Why does this happen? The key to understanding this is that when we loop over an empty collection, zero iterations occur and the loop body never executes. So when we call <code>my_sum([])</code>, first <code>sum_so_far</code> is assigned to <code>0</code>, and then the for loop does not execute any code, and so <code>0</code> is returned. A key observation here is that <em>when the collection is empty, the initial value of <code>sum_so_far</code> is returned</em>.</p>
<h2 id="designing-loops-using-the-accumulator-pattern">Designing loops using the accumulator pattern</h2>
<p>Our implementation of <code>my_sum</code> illustrates a more general pattern that well employ when we use loops to perform an aggregation computation. Here is the <strong>accumulator pattern</strong>:</p>
<ol type="1">
<li>Choose a meaningful name for an accumulator variable based on what youre computing. Use the suffix <code>_so_far</code> to remind yourself that this is an accumulator.</li>
<li>Pick an initial value for the accumulator. This value is usually what should be returned if the collection is empty.</li>
<li>In the loop body, update the accumulator variable based on the current value of the loop variable.</li>
<li>After the loop ends, return the accumulator.</li>
</ol>
<p>Here is a <em>code template</em> to illustrate this pattern.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1"></a><span class="op">&lt;</span>x<span class="op">&gt;</span>_so_far <span class="op">=</span> <span class="op">&lt;</span>default_value<span class="op">&gt;</span></span>
<span id="cb11-2"><a href="#cb11-2"></a></span>
<span id="cb11-3"><a href="#cb11-3"></a><span class="cf">for</span> element <span class="kw">in</span> <span class="op">&lt;</span>collection<span class="op">&gt;</span>:</span>
<span id="cb11-4"><a href="#cb11-4"></a> <span class="op">&lt;</span>x<span class="op">&gt;</span>_so_far <span class="op">=</span> ... <span class="op">&lt;</span>x<span class="op">&gt;</span>_so_far ... element ... <span class="co"># Somehow combine loop variable and accumulator</span></span>
<span id="cb11-5"><a href="#cb11-5"></a></span>
<span id="cb11-6"><a href="#cb11-6"></a><span class="cf">return</span> <span class="op">&lt;</span>x<span class="op">&gt;</span>_so_far</span></code></pre></div>
<p>Code templates are helpful when learning about programming techniques, as they give you a natural starting point in your code with “places to fill in”. However, as well see over the next few sections, we should not blindly follow code templates either. Part of mastering a code template is deciding when to use it and when to modify it to solve the problem at hand.</p>
<h3 id="accumulating-the-product">Accumulating the product</h3>
<p>Lets use the accumulator pattern to implement the function <code>product</code>:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1"></a><span class="kw">def</span> product(numbers: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb12-2"><a href="#cb12-2"></a> <span class="co">&quot;&quot;&quot;Return the product of the given numbers.</span></span>
<span id="cb12-3"><a href="#cb12-3"></a></span>
<span id="cb12-4"><a href="#cb12-4"></a><span class="co"> &gt;&gt;&gt; product([10, 20])</span></span>
<span id="cb12-5"><a href="#cb12-5"></a><span class="co"> 200</span></span>
<span id="cb12-6"><a href="#cb12-6"></a><span class="co"> &gt;&gt;&gt; product([-5, 4])</span></span>
<span id="cb12-7"><a href="#cb12-7"></a><span class="co"> -20</span></span>
<span id="cb12-8"><a href="#cb12-8"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb12-9"><a href="#cb12-9"></a> <span class="co"># ACCUMULATOR product_so_far: keep track of the product of the</span></span>
<span id="cb12-10"><a href="#cb12-10"></a> <span class="co"># elements in numbers seen so far in the loop.</span></span>
<span id="cb12-11"><a href="#cb12-11"></a> product_so_far <span class="op">=</span> <span class="dv">1</span></span>
<span id="cb12-12"><a href="#cb12-12"></a></span>
<span id="cb12-13"><a href="#cb12-13"></a> <span class="cf">for</span> number <span class="kw">in</span> numbers:</span>
<span id="cb12-14"><a href="#cb12-14"></a> product_so_far <span class="op">=</span> product_so_far <span class="op">*</span> number</span>
<span id="cb12-15"><a href="#cb12-15"></a></span>
<span id="cb12-16"><a href="#cb12-16"></a> <span class="cf">return</span> product_so_far</span></code></pre></div>
<p>Notice how similar the code for <code>product</code> is to <code>my_sum</code>. In fact, disregarding the changes in variable names, the only changes are:</p>
<ul>
<li>the initial value of the accumulator (<code>0</code> versus <code>1</code>)</li>
<li>the “update” operation inside the loop body (<code>+</code> versus <code>*</code>)</li>
</ul>
<h2 id="looping-over-sets">Looping over sets</h2>
<p>Because sets are collections, we can use for loops to iterate through the elements of a set as well. However, because sets are unordered, we cannot assume a particular order that the for loop will visit the elements in. So for loops over sets should only be used when <em>the same result would be obtained regardless of the order of the elements</em>. The aggregation functions weve looked at so far like <code>sum</code> satisfy this property.</p>
<h2 id="looping-over-strings">Looping over strings</h2>
<p>Strings are very similar to lists because they are considered ordered sequences of data. Python treats a string as an ordered collection of characters (strings of length one), and so we can use for loops with strings to iterate over its characters one at a time.</p>
<p>Here is an example of using a for loop to count the number of characters in a string.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1"></a><span class="kw">def</span> my_len(s: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb13-2"><a href="#cb13-2"></a> <span class="co">&quot;&quot;&quot;Return the number of characters in s.</span></span>
<span id="cb13-3"><a href="#cb13-3"></a></span>
<span id="cb13-4"><a href="#cb13-4"></a><span class="co"> &gt;&gt;&gt; my_len(&#39;David&#39;)</span></span>
<span id="cb13-5"><a href="#cb13-5"></a><span class="co"> 5</span></span>
<span id="cb13-6"><a href="#cb13-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb13-7"><a href="#cb13-7"></a> <span class="co"># ACCUMULATOR len_so_far: keep track of the number of</span></span>
<span id="cb13-8"><a href="#cb13-8"></a> <span class="co"># characters in s seen so far in the loop.</span></span>
<span id="cb13-9"><a href="#cb13-9"></a> len_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb13-10"><a href="#cb13-10"></a></span>
<span id="cb13-11"><a href="#cb13-11"></a> <span class="cf">for</span> character <span class="kw">in</span> s:</span>
<span id="cb13-12"><a href="#cb13-12"></a> len_so_far <span class="op">=</span> len_so_far <span class="op">+</span> <span class="dv">1</span></span>
<span id="cb13-13"><a href="#cb13-13"></a></span>
<span id="cb13-14"><a href="#cb13-14"></a> <span class="cf">return</span> len_so_far</span></code></pre></div>
<p>Unlike <code>my_sum</code>, here we do not use the loop variable to update the accumulator <code>len_so_far</code>. This is because we dont care what the actual value character is, we are only counting iterations. In these scenarios, we can use an underscore <code>_</code> in place of the name for the loop variable to communicate that the loop variable is not used in the body of the for loop:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1"></a><span class="kw">def</span> my_len(s: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb14-2"><a href="#cb14-2"></a> <span class="co">&quot;&quot;&quot;Return the number of characters in s.</span></span>
<span id="cb14-3"><a href="#cb14-3"></a></span>
<span id="cb14-4"><a href="#cb14-4"></a><span class="co"> &gt;&gt;&gt; my_len(&#39;David&#39;)</span></span>
<span id="cb14-5"><a href="#cb14-5"></a><span class="co"> 5</span></span>
<span id="cb14-6"><a href="#cb14-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb14-7"><a href="#cb14-7"></a> <span class="co"># ACCUMULATOR len_so_far: keep track of the number of</span></span>
<span id="cb14-8"><a href="#cb14-8"></a> <span class="co"># characters in s seen so far in the loop.</span></span>
<span id="cb14-9"><a href="#cb14-9"></a> len_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb14-10"><a href="#cb14-10"></a></span>
<span id="cb14-11"><a href="#cb14-11"></a> <span class="cf">for</span> _ <span class="kw">in</span> s:</span>
<span id="cb14-12"><a href="#cb14-12"></a> len_so_far <span class="op">=</span> len_so_far <span class="op">+</span> <span class="dv">1</span></span>
<span id="cb14-13"><a href="#cb14-13"></a></span>
<span id="cb14-14"><a href="#cb14-14"></a> <span class="cf">return</span> len_so_far</span></code></pre></div>
<h2 id="looping-over-dictionaries">Looping over dictionaries</h2>
<p>Python dictionaries are also iterable. Just like we saw with comprehensions, when we iterate over a dictionary, the loop variable refers to the <em>key</em> of each key-value pair. But of course, we can use the key to lookup its corresponding value in the dictionary.</p>
<p>For example, suppose we are given a dictionary mapping restaurant menu items (as strings) to their prices (as floats). Here is how we could calculate the sum of all the prices on the menu.</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1"></a><span class="kw">def</span> total_menu_price(menu: <span class="bu">dict</span>[<span class="bu">str</span>, <span class="bu">float</span>]) <span class="op">-&gt;</span> <span class="bu">float</span>:</span>
<span id="cb15-2"><a href="#cb15-2"></a> <span class="co">&quot;&quot;&quot;Return the total price of the given menu items.</span></span>
<span id="cb15-3"><a href="#cb15-3"></a></span>
<span id="cb15-4"><a href="#cb15-4"></a><span class="co"> &gt;&gt;&gt; total_menu_price({&#39;fries&#39;: 3.5, &#39;hamburger&#39;: 6.5})</span></span>
<span id="cb15-5"><a href="#cb15-5"></a><span class="co"> 10.0</span></span>
<span id="cb15-6"><a href="#cb15-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb15-7"><a href="#cb15-7"></a> <span class="co"># ACCUMULATOR total_so_far: keep track of the total cost of</span></span>
<span id="cb15-8"><a href="#cb15-8"></a> <span class="co"># all items in the menu seen so far in the loop.</span></span>
<span id="cb15-9"><a href="#cb15-9"></a> total_so_far <span class="op">=</span> <span class="fl">0.0</span></span>
<span id="cb15-10"><a href="#cb15-10"></a></span>
<span id="cb15-11"><a href="#cb15-11"></a> <span class="cf">for</span> item <span class="kw">in</span> menu:</span>
<span id="cb15-12"><a href="#cb15-12"></a> total_so_far <span class="op">=</span> total_so_far <span class="op">+</span> menu[item]</span>
<span id="cb15-13"><a href="#cb15-13"></a></span>
<span id="cb15-14"><a href="#cb15-14"></a> <span class="cf">return</span> total_so_far</span></code></pre></div>
<p>The loop variable <code>item</code> refers to the <em>keys</em> in the dictionary, so to access the corresponding prices we need to use a key lookup expression, <code>menu[item]</code>. Here is how we can visualize this using a loop accumulation table:</p>
<div class="reference-table">
<table>
<colgroup>
<col style="width: 16%" />
<col style="width: 34%" />
<col style="width: 49%" />
</colgroup>
<thead>
<tr class="header">
<th>Iteration</th>
<th>Loop variable (<code>item</code>)</th>
<th>Loop accumulator (<code>total_so_far</code>)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>0</td>
<td></td>
<td><code>0.0</code></td>
</tr>
<tr class="even">
<td>1</td>
<td><code>'fries'</code></td>
<td><code>6.5</code></td>
</tr>
<tr class="odd">
<td>2</td>
<td><code>'hamburger'</code></td>
<td><code>10.0</code></td>
</tr>
</tbody>
</table>
</div>
<p>One final note: like sets, dictionaries are unordered. We chose a particular order of keys for the loop accumulation table just to understand the loop behaviour, but we should not assume that this is the guaranteed order the keys would be visited. Just as with sets, only loop over dictionaries when your computation does <em>not</em> depend on the iteration order.</p>
<h2 id="a-new-type-annotation-iterable">A new type annotation: <code>Iterable</code></h2>
<p>Something you might notice about the two functions <code>my_len</code> and <code>my_sum</code> weve developed so far is that actually work on more types than currently specified by their parameter type annotation. For example, <code>my_len</code> works just as well on lists, sets, and other collections. If we look at the function body, we dont use the fact that <code>s</code> is a string at all—just that it can be iterated over. It would be nice if we could relax our type contract to allow for any collection argument value.</p>
<p>We say that a Python data type is <strong>iterable</strong> when its values can be used as the “collection” of a for loop, and that a Python object is iterable when it is an instance of an iterable data type.<label for="sn-4" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-4" class="margin-toggle"/><span class="sidenote"> You might wonder why Python doesnt just call these “collections” instead. There is a technical reason that is beyond the scope of this course, but for our purposes, well treat “iterable” and “collection” as synonymous.</span> This is equivalent to when a value can be used as the “collection” of a comprehension. We can import the <code>Iterable</code> type from <code>typing</code> to indicate that a value must be any data type that is iterable. Heres how we would write a more general <code>my_len</code>:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1"></a><span class="im">from</span> typing <span class="im">import</span> Iterable</span>
<span id="cb16-2"><a href="#cb16-2"></a></span>
<span id="cb16-3"><a href="#cb16-3"></a></span>
<span id="cb16-4"><a href="#cb16-4"></a><span class="kw">def</span> my_len(collection: Iterable) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb16-5"><a href="#cb16-5"></a> <span class="co">&quot;&quot;&quot;Return the number of elements in collection.</span></span>
<span id="cb16-6"><a href="#cb16-6"></a></span>
<span id="cb16-7"><a href="#cb16-7"></a><span class="co"> &gt;&gt;&gt; my_len(&#39;David&#39;)</span></span>
<span id="cb16-8"><a href="#cb16-8"></a><span class="co"> 5</span></span>
<span id="cb16-9"><a href="#cb16-9"></a><span class="co"> &gt;&gt;&gt; my_len([1, 2, 3])</span></span>
<span id="cb16-10"><a href="#cb16-10"></a><span class="co"> 3</span></span>
<span id="cb16-11"><a href="#cb16-11"></a><span class="co"> &gt;&gt;&gt; my_len({&#39;a&#39;: 1000})</span></span>
<span id="cb16-12"><a href="#cb16-12"></a><span class="co"> 1</span></span>
<span id="cb16-13"><a href="#cb16-13"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb16-14"><a href="#cb16-14"></a> len_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb16-15"><a href="#cb16-15"></a></span>
<span id="cb16-16"><a href="#cb16-16"></a> <span class="cf">for</span> _ <span class="kw">in</span> collection:</span>
<span id="cb16-17"><a href="#cb16-17"></a> len_so_far <span class="op">=</span> len_so_far <span class="op">+</span> <span class="dv">1</span></span>
<span id="cb16-18"><a href="#cb16-18"></a></span>
<span id="cb16-19"><a href="#cb16-19"></a> <span class="cf">return</span> len_so_far</span></code></pre></div>
<p>Notice that other than renaming a variable, we did not change the function body at all! This demonstrates how powerful the accumulator pattern can be; accumulators can work with any iterable object.</p>
<h3 id="alternatives-to-for-loops">Alternatives to for loops</h3>
<p>You may feel that several of the examples in this section are contrived. You are not wrong; we are trying to leverage your familiarity with the built-in functions to help introduce a new concept. You may also have noticed that there are other ways to solve some of the problems weve presented. For example, <code>average_menu_price</code> can be solved using comprehensions rather than loops:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb17-1"><a href="#cb17-1"></a><span class="kw">def</span> average_menu_price_v2(menu: <span class="bu">dict</span>[<span class="bu">str</span>, <span class="bu">float</span>]) <span class="op">-&gt;</span> <span class="bu">float</span>:</span>
<span id="cb17-2"><a href="#cb17-2"></a> <span class="co">&quot;&quot;&quot;Return the average price of an item from the menu.</span></span>
<span id="cb17-3"><a href="#cb17-3"></a></span>
<span id="cb17-4"><a href="#cb17-4"></a><span class="co"> &gt;&gt;&gt; average_menu_price({&#39;fries&#39;: 4.0, &#39;hamburger&#39;: 6.0})</span></span>
<span id="cb17-5"><a href="#cb17-5"></a><span class="co"> 5.0</span></span>
<span id="cb17-6"><a href="#cb17-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb17-7"><a href="#cb17-7"></a> prices <span class="op">=</span> [menu[item] <span class="cf">for</span> item <span class="kw">in</span> menu]</span>
<span id="cb17-8"><a href="#cb17-8"></a> <span class="cf">return</span> <span class="bu">sum</span>(prices) <span class="op">/</span> <span class="bu">len</span>(prices)</span></code></pre></div>
<p>Indeed, you have performed remarkably complex computations up to this point using just comprehensions to filter and transform data, and Pythons built-in functions to aggregate this data. For loops provide an alternate approach to these comprehensions that offer a trade-off of <em>code complexity</em> vs. <em>flexibility</em>. Comprehensions and built-in functions are often shorter and more direct translations of a computation than for loops, but for loops allow us to customize exactly how filtering and aggregation occurs.<label for="sn-5" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-5" class="margin-toggle"/><span class="sidenote"> A good rule of thumb to follow in this course is to use comprehensions and built-in functions when possible, and use loops when you really need a custom aggregation.</span></p>
<p>Of course, on your journey to learning programming it is important that you learn and master both of these techniques, and be able to translate between them when possible! Just as there are many ways to visualize a sunset (a painting, a photograph, a drawing, pixel art), so too are there many ways to implement a function. So whenever you see some code for a function involving comprehensions or loops, remember that you can always turn it into an additional learning opportunity by trying to rewrite it with a different approach.</p>
<h2 id="references">References</h2>
<ul>
<li>CSC108 videos: For loop over str (<a href="https://youtu.be/4OGtBt_VNCg">Part 1</a>, <a href="https://youtu.be/WRVDP152GEI">Part 2</a>, <a href="https://youtu.be/WRVDP152GEI">Part 3</a>)</li>
</ul>
</section>
<!--
(from outline)
Now let's see this in action in the PyCharm debugger.
We've now seen two ways to trace through this loop: writing (in English) what happens at each loop iteration, and tracing through with the PyCharm debugger.
The writing has the benefit that we can scan through to find each step and forces us to be very precise and detailed about analysing the code that's run, while the PyCharm debugger makes it very clear exactly what the values of the variables `sum_so_far` and `number` are and how they change over time, but we can only see one variable at a time (can't see history), and can only go forwards instead of backwards.
So we introduce a third approach that combines some of the key strengths of these two approaches (but also has some limitations): the *loop accumulation table*.
-->
<footer>
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
</footer>
</body>
</html>
+508
View File
@@ -0,0 +1,508 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>4.5 For Loop Variations</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="../tufte.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<div style="display:none">
\(
\newcommand{\NOT}{\neg}
\newcommand{\AND}{\wedge}
\newcommand{\OR}{\vee}
\newcommand{\XOR}{\oplus}
\newcommand{\IMP}{\Rightarrow}
\newcommand{\IFF}{\Leftrightarrow}
\newcommand{\TRUE}{\text{True}\xspace}
\newcommand{\FALSE}{\text{False}\xspace}
\newcommand{\IN}{\,{\in}\,}
\newcommand{\NOTIN}{\,{\notin}\,}
\newcommand{\TO}{\rightarrow}
\newcommand{\DIV}{\mid}
\newcommand{\NDIV}{\nmid}
\newcommand{\MOD}[1]{\pmod{#1}}
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
\newcommand{\N}{\mathbb N}
\newcommand{\Z}{\mathbb Z}
\newcommand{\Q}{\mathbb Q}
\newcommand{\R}{\mathbb R}
\newcommand{\C}{\mathbb C}
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
\newcommand{\cD}{\mathcal D}
\newcommand{\cE}{\mathcal E}
\newcommand{\cF}{\mathcal F}
\newcommand{\cG}{\mathcal G}
\newcommand{\cH}{\mathcal H}
\newcommand{\cI}{\mathcal I}
\newcommand{\cJ}{\mathcal J}
\newcommand{\cL}{\mathcal L}
\newcommand{\cK}{\mathcal K}
\newcommand{\cN}{\mathcal N}
\newcommand{\cO}{\mathcal O}
\newcommand{\cP}{\mathcal P}
\newcommand{\cQ}{\mathcal Q}
\newcommand{\cS}{\mathcal S}
\newcommand{\cT}{\mathcal T}
\newcommand{\cV}{\mathcal V}
\newcommand{\cW}{\mathcal W}
\newcommand{\cZ}{\mathcal Z}
\newcommand{\emp}{\emptyset}
\newcommand{\bs}{\backslash}
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
\newcommand{\abs}[1]{\left | #1 \right |}
\newcommand{\xspace}{}
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
\)
</div>
<header id="title-block-header">
<h1 class="title">4.5 For Loop Variations</h1>
</header>
<section>
<p>In the last section we introduced for loops and the accumulator pattern. The examples we used all had very similar code, with some differences in the type of collection we iterated over and how we initialized and updated our accumulator variable. In this section, well study two variations of the basic loop accumulator pattern: having multiple accumulator variables for the same loop, and using if statements to perform a <em>conditional update</em> of loop accumulators.</p>
<p>Before proceeding, please take moment to review the loop accumulator pattern:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="op">&lt;</span>x<span class="op">&gt;</span>_so_far <span class="op">=</span> <span class="op">&lt;</span>default_value<span class="op">&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2"></a></span>
<span id="cb1-3"><a href="#cb1-3"></a><span class="cf">for</span> element <span class="kw">in</span> <span class="op">&lt;</span>collection<span class="op">&gt;</span>:</span>
<span id="cb1-4"><a href="#cb1-4"></a> <span class="op">&lt;</span>x<span class="op">&gt;</span>_so_far <span class="op">=</span> ... <span class="op">&lt;</span>x<span class="op">&gt;</span>_so_far ... element ... <span class="co"># Somehow combine loop variable and accumulator</span></span>
<span id="cb1-5"><a href="#cb1-5"></a></span>
<span id="cb1-6"><a href="#cb1-6"></a><span class="cf">return</span> <span class="op">&lt;</span>x<span class="op">&gt;</span>_so_far</span></code></pre></div>
<h2 id="multiple-accumulators">Multiple accumulators</h2>
<p>In each example from the last section we used only one accumulator. The pattern can be extended to use multiple accumulators. For example, given a dictionary mapping menu items to prices, how can we get the average price? Remember that an average requires both the sum and the number of elements. We can create two accumulators to accomplish this:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">def</span> average_menu_price(menu: <span class="bu">dict</span>[<span class="bu">str</span>, <span class="bu">float</span>]) <span class="op">-&gt;</span> <span class="bu">float</span>:</span>
<span id="cb2-2"><a href="#cb2-2"></a> <span class="co">&quot;&quot;&quot;Return the average price of an item from the menu.</span></span>
<span id="cb2-3"><a href="#cb2-3"></a></span>
<span id="cb2-4"><a href="#cb2-4"></a><span class="co"> &gt;&gt;&gt; average_menu_price({&#39;fries&#39;: 3.5, &#39;hamburger&#39;: 6.5})</span></span>
<span id="cb2-5"><a href="#cb2-5"></a><span class="co"> 5.0</span></span>
<span id="cb2-6"><a href="#cb2-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-7"><a href="#cb2-7"></a> <span class="co"># ACCUMULATOR len_so_far: keep track of the number of</span></span>
<span id="cb2-8"><a href="#cb2-8"></a> <span class="co"># items in the menu seen so far in the loop.</span></span>
<span id="cb2-9"><a href="#cb2-9"></a> len_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb2-10"><a href="#cb2-10"></a> <span class="co"># ACCUMULATOR total_so_far: keep track of the cost of</span></span>
<span id="cb2-11"><a href="#cb2-11"></a> <span class="co"># all items in the menu seen so far in the loop.</span></span>
<span id="cb2-12"><a href="#cb2-12"></a> total_so_far <span class="op">=</span> <span class="fl">0.0</span></span>
<span id="cb2-13"><a href="#cb2-13"></a></span>
<span id="cb2-14"><a href="#cb2-14"></a> <span class="cf">for</span> item <span class="kw">in</span> menu:</span>
<span id="cb2-15"><a href="#cb2-15"></a> len_so_far <span class="op">=</span> len_so_far <span class="op">+</span> <span class="dv">1</span></span>
<span id="cb2-16"><a href="#cb2-16"></a> total_so_far <span class="op">=</span> total_so_far <span class="op">+</span> menu[item]</span>
<span id="cb2-17"><a href="#cb2-17"></a></span>
<span id="cb2-18"><a href="#cb2-18"></a> <span class="cf">return</span> total_so_far <span class="op">/</span> len_so_far</span></code></pre></div>
<p>Here is how we could write a loop accumulation table for this example:</p>
<div class="fullwidth reference-table">
<table>
<colgroup>
<col style="width: 12%" />
<col style="width: 26%" />
<col style="width: 29%" />
<col style="width: 31%" />
</colgroup>
<thead>
<tr class="header">
<th>Iteration</th>
<th>Loop variable (<code>item</code>)</th>
<th>Accumulator <code>len_so_far</code></th>
<th>Accumulator <code>total_so_far</code></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>0</td>
<td></td>
<td><code>0</code></td>
<td><code>0.0</code></td>
</tr>
<tr class="even">
<td>1</td>
<td><code>'fries'</code></td>
<td><code>1</code></td>
<td><code>6.5</code></td>
</tr>
<tr class="odd">
<td>2</td>
<td><code>'hamburger'</code></td>
<td><code>2</code></td>
<td><code>10.0</code></td>
</tr>
</tbody>
</table>
</div>
<h2 id="conditional-execution-of-the-accumulator">Conditional execution of the accumulator</h2>
<p>Consider the following problem: given a string, count the number of vowels in the string.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">def</span> count_vowels(s: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb3-2"><a href="#cb3-2"></a> <span class="co">&quot;&quot;&quot;Return the number of vowels in s.</span></span>
<span id="cb3-3"><a href="#cb3-3"></a></span>
<span id="cb3-4"><a href="#cb3-4"></a><span class="co"> &gt;&gt;&gt; count_vowels(&#39;aeiou&#39;)</span></span>
<span id="cb3-5"><a href="#cb3-5"></a><span class="co"> 5</span></span>
<span id="cb3-6"><a href="#cb3-6"></a><span class="co"> &gt;&gt;&gt; count_vowels(&#39;David&#39;)</span></span>
<span id="cb3-7"><a href="#cb3-7"></a><span class="co"> 2</span></span>
<span id="cb3-8"><a href="#cb3-8"></a><span class="co"> &quot;&quot;&quot;</span></span></code></pre></div>
<p>We saw in <a href="04-for-loops.html">4.4 Repeated Execution: For Loops</a> that we could count <em>every</em> character in a given string by using an accumulator that increased by 1 for every loop iteration. We can use the same idea for counting just vowels, but we need to increase the accumulator only when the current character is a vowel.</p>
<p>In Chapter 3, we learned how to control execution of whole blocks of code using if statements. By nesting an if statement inside a for loop, we can adapt our accumulator pattern to only update the accumulator when certain conditions are met.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">def</span> count_vowels(s: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb4-2"><a href="#cb4-2"></a> <span class="co">&quot;&quot;&quot;Return the number of vowels in s.</span></span>
<span id="cb4-3"><a href="#cb4-3"></a></span>
<span id="cb4-4"><a href="#cb4-4"></a><span class="co"> &gt;&gt;&gt; count_vowels(&#39;aeiou&#39;)</span></span>
<span id="cb4-5"><a href="#cb4-5"></a><span class="co"> 5</span></span>
<span id="cb4-6"><a href="#cb4-6"></a><span class="co"> &gt;&gt;&gt; count_vowels(&#39;David&#39;)</span></span>
<span id="cb4-7"><a href="#cb4-7"></a><span class="co"> 2</span></span>
<span id="cb4-8"><a href="#cb4-8"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-9"><a href="#cb4-9"></a> <span class="co"># ACCUMULATOR vowels_so_far: keep track of the number of vowels</span></span>
<span id="cb4-10"><a href="#cb4-10"></a> <span class="co"># seen so far in the loop.</span></span>
<span id="cb4-11"><a href="#cb4-11"></a> vowels_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb4-12"><a href="#cb4-12"></a></span>
<span id="cb4-13"><a href="#cb4-13"></a> <span class="cf">for</span> letter <span class="kw">in</span> s:</span>
<span id="cb4-14"><a href="#cb4-14"></a> <span class="cf">if</span> letter <span class="kw">in</span> <span class="st">&#39;aeiou&#39;</span>:</span>
<span id="cb4-15"><a href="#cb4-15"></a> vowels_so_far <span class="op">=</span> vowels_so_far <span class="op">+</span> <span class="dv">1</span></span>
<span id="cb4-16"><a href="#cb4-16"></a></span>
<span id="cb4-17"><a href="#cb4-17"></a> <span class="cf">return</span> vowels_so_far</span></code></pre></div>
<p>If <code>word</code> is the empty string, the for loop will not iterate once and the value 0 is returned. This tells us that we have initialized our accumulator correctly. What about the loop body? There are two cases to consider:</p>
<ol type="1">
<li>When <code>letter</code> is a vowel, the reassignment <code>vowels_so_far = vowels_so_far + 1</code> increases the number of vowels seen so far by 1.</li>
<li>When <code>letter</code> is not a vowel, nothing else happens in the current iteration because this if statement has no else branch. The vowel count remains the same.</li>
</ol>
<p>Heres our loop accumulation table for <code>count_vowels('David')</code>. At each iteration, the accumulator either stays the same (when <code>letter</code> is not a vowel) or increases by 1 (when <code>letter</code> is a vowel).</p>
<div class="reference-table">
<table>
<colgroup>
<col style="width: 23%" />
<col style="width: 34%" />
<col style="width: 41%" />
</colgroup>
<thead>
<tr class="header">
<th>Loop Iteration</th>
<th>Loop Variable <code>letter</code></th>
<th>Accumulator <code>vowels_so_far</code></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>0</td>
<td></td>
<td><code>0</code></td>
</tr>
<tr class="even">
<td>1</td>
<td><code>'D'</code></td>
<td><code>0</code></td>
</tr>
<tr class="odd">
<td>2</td>
<td><code>'a'</code></td>
<td><code>1</code></td>
</tr>
<tr class="even">
<td>3</td>
<td><code>'v'</code></td>
<td><code>1</code></td>
</tr>
<tr class="odd">
<td>4</td>
<td><code>'i'</code></td>
<td><code>2</code></td>
</tr>
<tr class="even">
<td>5</td>
<td><code>'d'</code></td>
<td><code>2</code></td>
</tr>
</tbody>
</table>
</div>
<!-- TODO: Add general control flow diagram of `count_vowels`-->
<p>We can also contrast this function to an equivalent implementation using a filtering comprehension:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw">def</span> count_vowels(s: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb5-2"><a href="#cb5-2"></a> <span class="co">&quot;&quot;&quot;Return the number of vowels in s.</span></span>
<span id="cb5-3"><a href="#cb5-3"></a></span>
<span id="cb5-4"><a href="#cb5-4"></a><span class="co"> &gt;&gt;&gt; count_vowels(&#39;aeiou&#39;)</span></span>
<span id="cb5-5"><a href="#cb5-5"></a><span class="co"> 5</span></span>
<span id="cb5-6"><a href="#cb5-6"></a><span class="co"> &gt;&gt;&gt; count_vowels(&#39;David&#39;)</span></span>
<span id="cb5-7"><a href="#cb5-7"></a><span class="co"> 2</span></span>
<span id="cb5-8"><a href="#cb5-8"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb5-9"><a href="#cb5-9"></a> <span class="cf">return</span> <span class="bu">len</span>([letter <span class="cf">for</span> letter <span class="kw">in</span> s <span class="cf">if</span> letter <span class="kw">in</span> <span class="st">&#39;aeiou&#39;</span>])</span></code></pre></div>
<p>This version hopefully makes clear that the <code>if letter in 'aeiou</code> in the loop version acts as a <em>filter</em> on the string <code>s</code>, causing the loop accumulator to only be updated for the vowels. In this version, the actual accumulation (<code>vowels_so_far = vowels_so_far + 1</code>) is handled by the call to <code>len</code>.</p>
<h3 id="implementing-max">Implementing <code>max</code></h3>
<p>Now lets consider implementing another built-in aggregation function: <code>max</code>. Well require that the input be non-empty, as we cannot compute the maximum element of an empty collection. This allows us to set the initial value of our accumulator based on the input.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="kw">def</span> my_max(numbers: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb6-2"><a href="#cb6-2"></a> <span class="co">&quot;&quot;&quot;Return the maximum value of the numbers in numbers.</span></span>
<span id="cb6-3"><a href="#cb6-3"></a></span>
<span id="cb6-4"><a href="#cb6-4"></a><span class="co"> Preconditions:</span></span>
<span id="cb6-5"><a href="#cb6-5"></a><span class="co"> - numbers != []</span></span>
<span id="cb6-6"><a href="#cb6-6"></a></span>
<span id="cb6-7"><a href="#cb6-7"></a><span class="co"> &gt;&gt;&gt; my_max([10, 20])</span></span>
<span id="cb6-8"><a href="#cb6-8"></a><span class="co"> 20</span></span>
<span id="cb6-9"><a href="#cb6-9"></a><span class="co"> &gt;&gt;&gt; my_max([-5, -4])</span></span>
<span id="cb6-10"><a href="#cb6-10"></a><span class="co"> -4</span></span>
<span id="cb6-11"><a href="#cb6-11"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb6-12"><a href="#cb6-12"></a> <span class="co"># ACCUMULATOR max_so_far: keep track of the maximum value</span></span>
<span id="cb6-13"><a href="#cb6-13"></a> <span class="co"># of the elements in numbers seen so far in the loop.</span></span>
<span id="cb6-14"><a href="#cb6-14"></a> max_so_far <span class="op">=</span> numbers[<span class="dv">0</span>]</span>
<span id="cb6-15"><a href="#cb6-15"></a></span>
<span id="cb6-16"><a href="#cb6-16"></a> <span class="cf">for</span> number <span class="kw">in</span> numbers:</span>
<span id="cb6-17"><a href="#cb6-17"></a> <span class="cf">if</span> number <span class="op">&gt;</span> max_so_far:</span>
<span id="cb6-18"><a href="#cb6-18"></a> max_so_far <span class="op">=</span> number</span>
<span id="cb6-19"><a href="#cb6-19"></a></span>
<span id="cb6-20"><a href="#cb6-20"></a> <span class="cf">return</span> max_so_far</span></code></pre></div>
<p>Because we can <em>assume</em> that the precondition holds when implementing <code>my_max</code>, we can access <code>numbers[0]</code> to set the initial value of <code>max_so_far</code> without worrying about getting an <code>IndexError</code>. In the loop, the accumulator <code>max_so_far</code> is updated only when a larger number is encountered (<code>if number &gt; max_so_far</code>). Note that here, the term <em>accumulator</em> diverges from its normal English meaning. At any point during the loop, <code>max_so_far</code> is assigned to a single list element, not some “accumulation” of all list elements see so far. Instead, <code>max_so_far</code> represents the <em>maximum of the elements seen so far</em>, and so what is being accumulated is a set of facts: “the elements seen so far all <code>&lt;= max_so_far</code>”.</p>
<h3 id="existential-search">Existential search</h3>
<p>In <a href="../03-logic/02-predicate-logic.html">3.2 Predicate Logic</a>, we saw how to use <code>any</code> to check whether there exists a string in a collection that starts with the letter <code>'D'</code>:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">def</span> starts_with(strings: Iterable[<span class="bu">str</span>], char: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb7-2"><a href="#cb7-2"></a> <span class="co">&quot;&quot;&quot;Return whether one of the given strings starts with the character char.</span></span>
<span id="cb7-3"><a href="#cb7-3"></a></span>
<span id="cb7-4"><a href="#cb7-4"></a><span class="co"> Precondition:</span></span>
<span id="cb7-5"><a href="#cb7-5"></a><span class="co"> - all({s != &#39;&#39; for s in strings})</span></span>
<span id="cb7-6"><a href="#cb7-6"></a><span class="co"> - len(char) == 1</span></span>
<span id="cb7-7"><a href="#cb7-7"></a></span>
<span id="cb7-8"><a href="#cb7-8"></a><span class="co"> &gt;&gt;&gt; starts_with([&#39;Hello&#39;, &#39;Goodbye&#39;, &#39;David&#39;, &#39;Dario&#39;], &#39;D&#39;)</span></span>
<span id="cb7-9"><a href="#cb7-9"></a><span class="co"> True</span></span>
<span id="cb7-10"><a href="#cb7-10"></a><span class="co"> &gt;&gt;&gt; starts_with([&#39;Hello&#39;, &#39;Goodbye&#39;, &#39;David&#39;, &#39;Dario&#39;], &#39;A&#39;)</span></span>
<span id="cb7-11"><a href="#cb7-11"></a><span class="co"> False</span></span>
<span id="cb7-12"><a href="#cb7-12"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb7-13"><a href="#cb7-13"></a> <span class="cf">return</span> <span class="bu">any</span>({s[<span class="dv">0</span>] <span class="op">==</span> char <span class="cf">for</span> s <span class="kw">in</span> words})</span></code></pre></div>
<p>Our next goal is to implement this function <em>without</em> using the <code>any</code> function, replacing it for loops and if statements. If we take a look at the argument to <code>any</code> above, we see some pretty big hints on how to do this:</p>
<ol type="1">
<li>The syntax <code>for s in words</code> can be used to create a for loop.</li>
<li>The expression <code>s[0] == char</code> can be used as a condition for an if statement.</li>
</ol>
<p>Lets give it a shot using our existing accumulator pattern. Because the result of the function is a <code>bool</code>, our accumulator will also be a <code>bool</code>. Its initial value will be <code>False</code>, which is the correct return value when <code>strings</code> is empty.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1"></a><span class="kw">def</span> starts_with_v2(words: <span class="bu">list</span>[<span class="bu">str</span>], char: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb8-2"><a href="#cb8-2"></a> <span class="co">&quot;&quot;&quot;...&quot;&quot;&quot;</span></span>
<span id="cb8-3"><a href="#cb8-3"></a> <span class="co"># ACCUMULATOR starts_with_so_far: keep track of whether</span></span>
<span id="cb8-4"><a href="#cb8-4"></a> <span class="co"># any of the words seen by the loop so far starts with char.</span></span>
<span id="cb8-5"><a href="#cb8-5"></a> starts_with_so_far <span class="op">=</span> <span class="va">False</span></span>
<span id="cb8-6"><a href="#cb8-6"></a></span>
<span id="cb8-7"><a href="#cb8-7"></a> <span class="cf">for</span> s <span class="kw">in</span> words:</span>
<span id="cb8-8"><a href="#cb8-8"></a> ...</span>
<span id="cb8-9"><a href="#cb8-9"></a></span>
<span id="cb8-10"><a href="#cb8-10"></a> <span class="cf">return</span> starts_with_so_far</span></code></pre></div>
<p>How do we update the accumulator? We set it to <code>True</code> when the current string <code>s</code> starts with <code>char</code>, which is exactly the condition from the comprehension.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1"></a><span class="kw">def</span> starts_with_v2(strings: Iterable[<span class="bu">str</span>], char: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb9-2"><a href="#cb9-2"></a> <span class="co">&quot;&quot;&quot;...&quot;&quot;&quot;</span></span>
<span id="cb9-3"><a href="#cb9-3"></a> <span class="co"># ACCUMULATOR starts_with_so_far: keep track of whether</span></span>
<span id="cb9-4"><a href="#cb9-4"></a> <span class="co"># any of the strings seen by the loop so far starts with char.</span></span>
<span id="cb9-5"><a href="#cb9-5"></a> starts_with_so_far <span class="op">=</span> <span class="va">False</span></span>
<span id="cb9-6"><a href="#cb9-6"></a></span>
<span id="cb9-7"><a href="#cb9-7"></a> <span class="cf">for</span> s <span class="kw">in</span> strings:</span>
<span id="cb9-8"><a href="#cb9-8"></a> <span class="cf">if</span> s[<span class="dv">0</span>] <span class="op">==</span> char:</span>
<span id="cb9-9"><a href="#cb9-9"></a> starts_with_so_far <span class="op">=</span> <span class="va">True</span></span>
<span id="cb9-10"><a href="#cb9-10"></a></span>
<span id="cb9-11"><a href="#cb9-11"></a> <span class="cf">return</span> starts_with_so_far</span></code></pre></div>
<p>Here is a loop accumulation table for <code>starts_with(['Hello', 'Goodbye', 'David', 'Mario'], 'D')</code>. The third iteration assigns <code>starts_with_so_far</code> to <code>True</code>, while in the other iterations nothing occurs.</p>
<div class="reference-table">
<table style="width:93%;">
<colgroup>
<col style="width: 16%" />
<col style="width: 27%" />
<col style="width: 48%" />
</colgroup>
<thead>
<tr class="header">
<th>Iteration</th>
<th>Loop variable <code>s</code></th>
<th>Accumulator <code>starts_with_so_far</code></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>0</td>
<td></td>
<td><code>False</code></td>
</tr>
<tr class="even">
<td>1</td>
<td><code>'Hello'</code></td>
<td><code>False</code></td>
</tr>
<tr class="odd">
<td>2</td>
<td><code>'Goodbye'</code></td>
<td><code>False</code></td>
</tr>
<tr class="even">
<td>3</td>
<td><code>'David'</code></td>
<td><code>True</code></td>
</tr>
<tr class="odd">
<td>4</td>
<td><code>'Mario'</code></td>
<td><code>True</code></td>
</tr>
</tbody>
</table>
</div>
<h3 id="early-returns">Early returns</h3>
<p>The function <code>starts_with_v2</code> is correct and fits our accumulator pattern well. But you might have noticed that it performs unnecessary work because it must loop through every element of the collection before returning a result. Why is this unnecessary? Because we are interested only in whether <em>there exists</em> a string that starts with the given letter! As soon as the condition <code>s[0] == char</code> evaluates to <code>True</code>, we know that the answer is <em>Yes</em> without checking any of the remaining strings.</p>
<p>So the question is, how do we take advantage of this observation to make our code more efficient? We can use a return statement inside the body of the loop. Lets revisit how we described the execution of a return statement in Chapter 2 (new emphasis in <strong>bold</strong>):</p>
<blockquote>
<p>When a return statement is executed, the following happens:</p>
</blockquote>
<blockquote>
<ol type="1">
<li>The <code>&lt;expression&gt;</code> is evaluated, producing a value.</li>
<li>That value is then returned to wherever the function was called. <strong>No more code in the function body is executed after this point.</strong>"</li>
</ol>
</blockquote>
<p>In all our functions so far, we have written return statements only at the end of our function bodies or branches of an if statement. This should make sense based on the behaviour described above: any code after a return statement will not execute!</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1"></a><span class="cf">return</span> <span class="dv">5</span></span>
<span id="cb10-2"><a href="#cb10-2"></a>x <span class="op">=</span> <span class="dv">10</span> <span class="co"># This statement doesn&#39;t execute!</span></span></code></pre></div>
<p>But we can combine return statements with if statements to conditionally stop executing any more code in the function body. This is called <em>short-circuiting</em> or <em>early returning</em>.</p>
<p>So our first attempt at making a more efficient <code>starts_with</code> is to use an early return inside the if branch:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1"></a><span class="kw">def</span> starts_with_v3(strings: Iterable[<span class="bu">str</span>], char: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb11-2"><a href="#cb11-2"></a> <span class="co">&quot;&quot;&quot;...&quot;&quot;&quot;</span></span>
<span id="cb11-3"><a href="#cb11-3"></a> <span class="cf">for</span> s <span class="kw">in</span> strings:</span>
<span id="cb11-4"><a href="#cb11-4"></a> <span class="cf">if</span> s[<span class="dv">0</span>] <span class="op">==</span> char:</span>
<span id="cb11-5"><a href="#cb11-5"></a> <span class="cf">return</span> <span class="va">True</span></span></code></pre></div>
<!-- TODO: Add control flow diagram of `starts_with_v3`, showing that there is a path from the for loop to no return statement (or a path from the for loop to the default return statement: return None)-->
<p>This for loop is strange: it seems we no longer have an accumulator variable! This is actually fairly common for functions that return booleans. Rather than accumulating a <code>True</code>/<code>False</code> value, it is often possible to directly return the literals <code>True</code> or <code>False</code>.</p>
<p>The <code>starts_with_v3</code> implementation does successfully return <code>True</code> on our first doctest example during the <em>third</em> loop iteration (when <code>s = 'David'</code>), skipping the fourth iteration. However, this implementation will fail the second doctest example (when there are no strings that start with the given character in the collection). We have not explicitly stated what to return when <em>none</em> of the strings in <code>words</code> starts with <code>char</code>. Actually, we have <em>violated our own type contract</em> because the function will implicitly return <code>None</code> in this scenario.</p>
<p>To fix it, we need to specify what to return if the loop stops without retuning early—this occurs only when there are no strings that start with the given character, and so we return <code>False</code>.</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1"></a><span class="kw">def</span> starts_with_v4(strings: Iterable[<span class="bu">str</span>], char: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb12-2"><a href="#cb12-2"></a> <span class="co">&quot;&quot;&quot;...&quot;&quot;&quot;</span></span>
<span id="cb12-3"><a href="#cb12-3"></a> <span class="cf">for</span> s <span class="kw">in</span> strings:</span>
<span id="cb12-4"><a href="#cb12-4"></a> <span class="cf">if</span> s[<span class="dv">0</span>] <span class="op">==</span> char:</span>
<span id="cb12-5"><a href="#cb12-5"></a> <span class="cf">return</span> <span class="va">True</span></span>
<span id="cb12-6"><a href="#cb12-6"></a></span>
<span id="cb12-7"><a href="#cb12-7"></a> <span class="cf">return</span> <span class="va">False</span></span></code></pre></div>
<!-- TODO: Add control flow diagram of `starts_with_v4`.-->
<h3 id="one-common-error">One common error</h3>
<p>When working with early returns inside loops, students often have a tendency to write symmetric if-else branches, like the following:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1"></a><span class="kw">def</span> starts_with_v5(strings: Iterable[<span class="bu">str</span>], char: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb13-2"><a href="#cb13-2"></a> <span class="co">&quot;&quot;&quot;...&quot;&quot;&quot;</span></span>
<span id="cb13-3"><a href="#cb13-3"></a> <span class="cf">for</span> s <span class="kw">in</span> strings:</span>
<span id="cb13-4"><a href="#cb13-4"></a> <span class="cf">if</span> s[<span class="dv">0</span>] <span class="op">==</span> char:</span>
<span id="cb13-5"><a href="#cb13-5"></a> <span class="cf">return</span> <span class="va">True</span></span>
<span id="cb13-6"><a href="#cb13-6"></a> <span class="cf">else</span>:</span>
<span id="cb13-7"><a href="#cb13-7"></a> <span class="cf">return</span> <span class="va">False</span></span></code></pre></div>
<p>Unfortunately, while we emphasized symmetry earlier when writing functions with if statements, here symmetry is <em>not</em> desirable! With both the if and else branches containing an early return, the loop will only ever perform one iteration. That is, <code>starts_with_v5</code> makes a decision about whether to return <code>True</code> or <code>False</code> just by examining the first string in the collection, regardless of what the other strings are. So if we consider <code>starts_with_v5(['Hello', 'Goodbye', 'David', 'Mario'], 'D')</code>, the only string to be visited in the loop is <code>'Hello'</code>, and <code>False</code> would be returned!</p>
<p>The lesson here is that existential searches are fundamentally asymmetric: your function can return <code>True</code> early as soon as it has found an element of the collection meeting the desired criterion, but to return <code>False</code> it must check <em>every</em> element of the collection.</p>
<!-- TODO: Add control flow diagram of `starts_with_v5`, showing that there is no way to loop.-->
<h3 id="universal-search">Universal search</h3>
<p>Now lets consider a dual problem to the previous one: given a collection of strings and a character, return whether <em>all</em> strings in the collect start with that letter. If we use the comprehension version of <code>starts_with</code>, this change is as simple as swapping the <code>any</code> for <code>all</code>:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1"></a><span class="kw">def</span> all_start_with(strings: Iterable[<span class="bu">str</span>], char: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb14-2"><a href="#cb14-2"></a> <span class="co">&quot;&quot;&quot;Return whether all of the given strings starts with the character char.</span></span>
<span id="cb14-3"><a href="#cb14-3"></a></span>
<span id="cb14-4"><a href="#cb14-4"></a><span class="co"> Precondition:</span></span>
<span id="cb14-5"><a href="#cb14-5"></a><span class="co"> - all({s != &#39;&#39; for s in strings})</span></span>
<span id="cb14-6"><a href="#cb14-6"></a><span class="co"> - len(char) == 1</span></span>
<span id="cb14-7"><a href="#cb14-7"></a></span>
<span id="cb14-8"><a href="#cb14-8"></a><span class="co"> &gt;&gt;&gt; all_starts_with([&#39;Hello&#39;, &#39;Goodbye&#39;, &#39;David&#39;, &#39;Dario&#39;], &#39;D&#39;)</span></span>
<span id="cb14-9"><a href="#cb14-9"></a><span class="co"> False</span></span>
<span id="cb14-10"><a href="#cb14-10"></a><span class="co"> &gt;&gt;&gt; all_starts_with([&#39;Drip&#39;, &#39;Drop&#39;, &#39;Dangle&#39;], &#39;D&#39;)</span></span>
<span id="cb14-11"><a href="#cb14-11"></a><span class="co"> True</span></span>
<span id="cb14-12"><a href="#cb14-12"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb14-13"><a href="#cb14-13"></a> <span class="cf">return</span> <span class="bu">all</span>({s[<span class="dv">0</span>] <span class="op">==</span> char <span class="cf">for</span> s <span class="kw">in</span> strings})</span></code></pre></div>
<p>We can also use the accumulator pattern from <code>starts_with_v2</code> to check every string. Now, our accumulator starts with the default value of <code>True</code>, and changes to <code>False</code> when the loop encounters a string that does <em>not</em> start with the given letter.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> Such a string acts as a <em>counterexample</em> to the statement “every string starts with the given character”.</span></p>
<div class="sourceCode" id="cb15"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1"></a><span class="kw">def</span> all_starts_with_v2(strings: Iterable[<span class="bu">str</span>], char: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb15-2"><a href="#cb15-2"></a> <span class="co">&quot;&quot;&quot;...&quot;&quot;&quot;</span></span>
<span id="cb15-3"><a href="#cb15-3"></a> <span class="co"># ACCUMULATOR starts_with_so_far: keep track of whether</span></span>
<span id="cb15-4"><a href="#cb15-4"></a> <span class="co"># all of the strings seen by the loop so far starts with char.</span></span>
<span id="cb15-5"><a href="#cb15-5"></a> starts_with_so_far <span class="op">=</span> <span class="va">True</span></span>
<span id="cb15-6"><a href="#cb15-6"></a></span>
<span id="cb15-7"><a href="#cb15-7"></a> <span class="cf">for</span> s <span class="kw">in</span> strings:</span>
<span id="cb15-8"><a href="#cb15-8"></a> <span class="cf">if</span> s[<span class="dv">0</span>] <span class="op">!=</span> char:</span>
<span id="cb15-9"><a href="#cb15-9"></a> starts_with_so_far <span class="op">=</span> <span class="va">False</span></span>
<span id="cb15-10"><a href="#cb15-10"></a></span>
<span id="cb15-11"><a href="#cb15-11"></a> <span class="cf">return</span> starts_with_so_far</span></code></pre></div>
<p>And as before, we can also write this function using an early return, since we can return <code>False</code> as soon as a counterexample is found:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1"></a><span class="kw">def</span> all_starts_with_v3(strings: Iterable[<span class="bu">str</span>], char: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb16-2"><a href="#cb16-2"></a> <span class="co">&quot;&quot;&quot;...&quot;&quot;&quot;</span></span>
<span id="cb16-3"><a href="#cb16-3"></a> <span class="cf">for</span> s <span class="kw">in</span> words:</span>
<span id="cb16-4"><a href="#cb16-4"></a> <span class="cf">if</span> s[<span class="dv">0</span>] <span class="op">!=</span> char:</span>
<span id="cb16-5"><a href="#cb16-5"></a> <span class="cf">return</span> <span class="va">False</span></span>
<span id="cb16-6"><a href="#cb16-6"></a></span>
<span id="cb16-7"><a href="#cb16-7"></a> <span class="cf">return</span> <span class="va">True</span></span></code></pre></div>
<p>Note that this code is very similar to <code>starts_with_v4</code>, except the condition has been negated and the <code>True</code> and <code>False</code> swapped. Existential and universal search are very closely related, and this is borne out by the similarities in these two functions. However, this also illustrates the fact that loops are more complex than using built-in functions and comprehensions: before, we could just swap <code>any</code> for <code>all</code>, but with loops we have to change a few different areas of the code to make this change.</p>
</section>
<!--
We've seen short-circuiting before.
For example, when performing an `and` operation and the left-hand value evaluates to `False`, there is no need to test the right-hand value.
Similarly, the built-in functions `any` and `all` don't need to test all the values in a collection.
For example, as soon as the condition given to `any` evaluates to `True` for some element in the collection, the remaining elements do not need to be tested.
Let's look at how to implement our own, simplified versions of `any` and `all` using for loops, if statements, and return statements.
-->
<footer>
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
</footer>
</body>
</html>
+365
View File
@@ -0,0 +1,365 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>4.6 Index-Based For Loops</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="../tufte.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<div style="display:none">
\(
\newcommand{\NOT}{\neg}
\newcommand{\AND}{\wedge}
\newcommand{\OR}{\vee}
\newcommand{\XOR}{\oplus}
\newcommand{\IMP}{\Rightarrow}
\newcommand{\IFF}{\Leftrightarrow}
\newcommand{\TRUE}{\text{True}\xspace}
\newcommand{\FALSE}{\text{False}\xspace}
\newcommand{\IN}{\,{\in}\,}
\newcommand{\NOTIN}{\,{\notin}\,}
\newcommand{\TO}{\rightarrow}
\newcommand{\DIV}{\mid}
\newcommand{\NDIV}{\nmid}
\newcommand{\MOD}[1]{\pmod{#1}}
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
\newcommand{\N}{\mathbb N}
\newcommand{\Z}{\mathbb Z}
\newcommand{\Q}{\mathbb Q}
\newcommand{\R}{\mathbb R}
\newcommand{\C}{\mathbb C}
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
\newcommand{\cD}{\mathcal D}
\newcommand{\cE}{\mathcal E}
\newcommand{\cF}{\mathcal F}
\newcommand{\cG}{\mathcal G}
\newcommand{\cH}{\mathcal H}
\newcommand{\cI}{\mathcal I}
\newcommand{\cJ}{\mathcal J}
\newcommand{\cL}{\mathcal L}
\newcommand{\cK}{\mathcal K}
\newcommand{\cN}{\mathcal N}
\newcommand{\cO}{\mathcal O}
\newcommand{\cP}{\mathcal P}
\newcommand{\cQ}{\mathcal Q}
\newcommand{\cS}{\mathcal S}
\newcommand{\cT}{\mathcal T}
\newcommand{\cV}{\mathcal V}
\newcommand{\cW}{\mathcal W}
\newcommand{\cZ}{\mathcal Z}
\newcommand{\emp}{\emptyset}
\newcommand{\bs}{\backslash}
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
\newcommand{\abs}[1]{\left | #1 \right |}
\newcommand{\xspace}{}
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
\)
</div>
<header id="title-block-header">
<h1 class="title">4.6 Index-Based For Loops</h1>
</header>
<section>
<p>We have learned a lot about collections so far:</p>
<ol type="1">
<li>We can access the elements of a collection via indexing (e.g., for lists and strings) or key lookups (e.g., for dictionaries).</li>
<li>We can evaluate an expression for each element of a collection with comprehensions to produce a new collection.</li>
<li>We can execute a set of statements for each element of a collection with a for loop.</li>
</ol>
<p>The loops we have worked with so far are element-based, meaning the loop variable refers to a specific element in the collection. Though these loops are powerful, they have one limitation: they process each element of the collection independent of where they appear in the collection. In this section, well see how we can loop through elements of index-based collections while keeping track of the current index. Looping by index enables us to solve more problems than looping by element alone, because well be able to take into account <em>where</em> a particular element is in a collection in the loop body.</p>
<p>As in the previous section, before proceeding please take a moment to review the basic loop accumulator pattern:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="op">&lt;</span>x<span class="op">&gt;</span>_so_far <span class="op">=</span> <span class="op">&lt;</span>default_value<span class="op">&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2"></a></span>
<span id="cb1-3"><a href="#cb1-3"></a><span class="cf">for</span> element <span class="kw">in</span> <span class="op">&lt;</span>collection<span class="op">&gt;</span>:</span>
<span id="cb1-4"><a href="#cb1-4"></a> <span class="op">&lt;</span>x<span class="op">&gt;</span>_so_far <span class="op">=</span> ... <span class="op">&lt;</span>x<span class="op">&gt;</span>_so_far ... element ... <span class="co"># Somehow combine loop variable and accumulator</span></span>
<span id="cb1-5"><a href="#cb1-5"></a></span>
<span id="cb1-6"><a href="#cb1-6"></a><span class="cf">return</span> <span class="op">&lt;</span>x<span class="op">&gt;</span>_so_far</span></code></pre></div>
<h2 id="remembering-the-problem-repeating-code">Remembering the problem: repeating code</h2>
<p>When we introduced for loops, we presented a <code>my_sum</code> implementation that showed the exact statement that is repeated:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">def</span> my_sum(numbers: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb2-2"><a href="#cb2-2"></a> <span class="co">&quot;&quot;&quot;Return the sum of the given numbers.</span></span>
<span id="cb2-3"><a href="#cb2-3"></a></span>
<span id="cb2-4"><a href="#cb2-4"></a><span class="co"> &gt;&gt;&gt; my_sum([10, 20, 30])</span></span>
<span id="cb2-5"><a href="#cb2-5"></a><span class="co"> 60</span></span>
<span id="cb2-6"><a href="#cb2-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-7"><a href="#cb2-7"></a> sum_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb2-8"><a href="#cb2-8"></a></span>
<span id="cb2-9"><a href="#cb2-9"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> numbers[<span class="dv">0</span>]</span>
<span id="cb2-10"><a href="#cb2-10"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> numbers[<span class="dv">1</span>]</span>
<span id="cb2-11"><a href="#cb2-11"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> numbers[<span class="dv">2</span>]</span>
<span id="cb2-12"><a href="#cb2-12"></a></span>
<span id="cb2-13"><a href="#cb2-13"></a> <span class="cf">return</span> sum_so_far</span></code></pre></div>
<p>Our eventual solution to the <code>my_sum</code> function used a loop variable, <code>number</code>, in place of the <code>numbers[_]</code> in the body. There is another solution if we observe that the indexes being used start at <code>0</code> and increase by one on each iteration of the loop. On the last iteration, the index should be: <code>len(numbers) - 1</code>. This sequence of numbers can be expressed using the <code>range</code> data type: <code>range(0, len(numbers))</code> Based on this, let us use a different kind of for loop to implement <code>my_sum</code>:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">def</span> my_sum(numbers: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb3-2"><a href="#cb3-2"></a> <span class="co">&quot;&quot;&quot;Return the sum of the given numbers.</span></span>
<span id="cb3-3"><a href="#cb3-3"></a></span>
<span id="cb3-4"><a href="#cb3-4"></a><span class="co"> &gt;&gt;&gt; my_sum([10, 20, 30])</span></span>
<span id="cb3-5"><a href="#cb3-5"></a><span class="co"> 60</span></span>
<span id="cb3-6"><a href="#cb3-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb3-7"><a href="#cb3-7"></a> <span class="co"># ACCUMULATOR sum_so_far: keep track of the running sum of the elements in numbers.</span></span>
<span id="cb3-8"><a href="#cb3-8"></a> sum_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb3-9"><a href="#cb3-9"></a></span>
<span id="cb3-10"><a href="#cb3-10"></a> <span class="cf">for</span> number <span class="kw">in</span> numbers:</span>
<span id="cb3-11"><a href="#cb3-11"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> number</span>
<span id="cb3-12"><a href="#cb3-12"></a></span>
<span id="cb3-13"><a href="#cb3-13"></a> <span class="cf">return</span> sum_so_far</span>
<span id="cb3-14"><a href="#cb3-14"></a></span>
<span id="cb3-15"><a href="#cb3-15"></a></span>
<span id="cb3-16"><a href="#cb3-16"></a><span class="kw">def</span> my_sum_v2(numbers: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb3-17"><a href="#cb3-17"></a> <span class="co">&quot;&quot;&quot;Return the sum of the given numbers.</span></span>
<span id="cb3-18"><a href="#cb3-18"></a></span>
<span id="cb3-19"><a href="#cb3-19"></a><span class="co"> &gt;&gt;&gt; my_sum_v2([10, 20, 30])</span></span>
<span id="cb3-20"><a href="#cb3-20"></a><span class="co"> 60</span></span>
<span id="cb3-21"><a href="#cb3-21"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb3-22"><a href="#cb3-22"></a> <span class="co"># ACCUMULATOR sum_so_far: keep track of the running sum of the elements in numbers.</span></span>
<span id="cb3-23"><a href="#cb3-23"></a> sum_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb3-24"><a href="#cb3-24"></a></span>
<span id="cb3-25"><a href="#cb3-25"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">0</span>, <span class="bu">len</span>(numbers)):</span>
<span id="cb3-26"><a href="#cb3-26"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> numbers[i]</span>
<span id="cb3-27"><a href="#cb3-27"></a></span>
<span id="cb3-28"><a href="#cb3-28"></a> <span class="cf">return</span> sum_so_far</span></code></pre></div>
<p>Both <code>my_sum</code> and <code>my_sum_v2</code> use the accumulator pattern, and in fact initialize and update the accumulator in the exact same way. But there are some key differences in how their loops are structured:</p>
<ul>
<li>Loop variable <code>number</code> vs. <code>i</code>: <code>number</code> refers to an element of the list <code>numbers</code> (starting with the first element); <code>i</code> refers to an integer (starting at 0).</li>
<li>Looping over a <code>list</code> vs. a <code>range</code>: <code>for number in numbers</code> causes the loop body to execute once for each element in <code>numbers</code>. <code>for i in range(0, len(numbers))</code> causes the loop body to execute once for each integer in <code>range(0, len(numbers))</code>.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> Because the range “stop” argument is exclusive, these two versions both cause the same number of iterations, equal to the number of elements in <code>numbers</code>.</span></li>
<li>Updating the accumulator: since <code>number</code> refers to a list element, we can add it directly to the accumulator. Since <code>i</code> refers to <em>where</em> we are in the list, we access the corresponding list element using list indexing to add it to the accumulator.</li>
</ul>
<p>In the case of <code>my_sum</code>, both our element-based and index-based implementations are correct. However, our next example illustrates a situation where the loop <em>must</em> know the index of the current element in order to solve the given problem.</p>
<h2 id="when-location-matters">When location matters</h2>
<p>Consider the following problem: given a string, count the number of times in the string two adjacent characters are equal. For example, the string <code>'look'</code> has two adjacent <code>'o'</code>s, and the string <code>'David'</code> has no repeated adjacent characters. The location of the characters matters; even though the string <code>'canal'</code> has two <code>'a'</code> characters, they are not adjacent</p>
<p>Lets use these examples to design our function:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">def</span> count_adjacent_repeats(string: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb4-2"><a href="#cb4-2"></a> <span class="co">&quot;&quot;&quot;Return the number of times in the given string that two adjacent characters are equal.</span></span>
<span id="cb4-3"><a href="#cb4-3"></a></span>
<span id="cb4-4"><a href="#cb4-4"></a><span class="co"> &gt;&gt;&gt; count_adjacent_repeats(&#39;look&#39;)</span></span>
<span id="cb4-5"><a href="#cb4-5"></a><span class="co"> 1</span></span>
<span id="cb4-6"><a href="#cb4-6"></a><span class="co"> &gt;&gt;&gt; count_adjacent_repeats(&#39;David&#39;)</span></span>
<span id="cb4-7"><a href="#cb4-7"></a><span class="co"> 0</span></span>
<span id="cb4-8"><a href="#cb4-8"></a><span class="co"> &gt;&gt;&gt; count_adjacent_repeats(&#39;canal&#39;)</span></span>
<span id="cb4-9"><a href="#cb4-9"></a><span class="co"> 0</span></span>
<span id="cb4-10"><a href="#cb4-10"></a><span class="co"> &quot;&quot;&quot;</span></span></code></pre></div>
<p>Before we try to implement this function, lets reason about how we might approach the problem. First, as this is a “counting” problem, a natural fit would be to use an accumulator variable <code>repeats_so_far</code> that starts at 0 and increases by 1 every time two adjacent repeated characters are found. We dont know where the characters in the string may be repeated, so we must start at the beginning and continue to the end. In addition, we are comparing adjacent characters, so we need two indices every loop iteration:</p>
<table>
<thead>
<tr class="header">
<th>Comparison</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>string[0] == string[1]</code></td>
</tr>
<tr class="even">
<td><code>string[1] == string[2]</code></td>
</tr>
<tr class="odd">
<td><code>string[2] == string[3]</code></td>
</tr>
<tr class="even">
<td></td>
</tr>
</tbody>
</table>
<p>Notice that the indices to the left of the <code>==</code> operator start at 0 and increase by 1. Similarly, the indices to the right of the <code>==</code> operator start at 1 and increase by 1. Does this mean we need to use two for loops and two <code>range</code>s? No. We should also notice that the index to the right of <code>==</code> is always larger than the left by 1, so we have a way of calculating the right index from the left index. Here is out first attempt.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw">def</span> count_adjacent_repeats(string: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb5-2"><a href="#cb5-2"></a> <span class="co">&quot;&quot;&quot;Return the number of repeated adjacent characters in string.</span></span>
<span id="cb5-3"><a href="#cb5-3"></a></span>
<span id="cb5-4"><a href="#cb5-4"></a><span class="co"> &gt;&gt;&gt; count_adjacent_repeats(&#39;look&#39;)</span></span>
<span id="cb5-5"><a href="#cb5-5"></a><span class="co"> 1</span></span>
<span id="cb5-6"><a href="#cb5-6"></a><span class="co"> &gt;&gt;&gt; count_adjacent_repeats(&#39;David&#39;)</span></span>
<span id="cb5-7"><a href="#cb5-7"></a><span class="co"> 0</span></span>
<span id="cb5-8"><a href="#cb5-8"></a><span class="co"> &gt;&gt;&gt; count_adjacent_repeats(&#39;canal&#39;)</span></span>
<span id="cb5-9"><a href="#cb5-9"></a><span class="co"> 0</span></span>
<span id="cb5-10"><a href="#cb5-10"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb5-11"><a href="#cb5-11"></a> <span class="co"># ACCUMULATOR repeats_so_far: keep track of the number of adjacent</span></span>
<span id="cb5-12"><a href="#cb5-12"></a> <span class="co"># characters that are identical</span></span>
<span id="cb5-13"><a href="#cb5-13"></a> repeats_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb5-14"><a href="#cb5-14"></a></span>
<span id="cb5-15"><a href="#cb5-15"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">0</span>, <span class="bu">len</span>(string)):</span>
<span id="cb5-16"><a href="#cb5-16"></a> <span class="cf">if</span> string[i] <span class="op">==</span> string[i <span class="op">+</span> <span class="dv">1</span>]:</span>
<span id="cb5-17"><a href="#cb5-17"></a> repeats_so_far <span class="op">=</span> repeats_so_far <span class="op">+</span> <span class="dv">1</span></span>
<span id="cb5-18"><a href="#cb5-18"></a></span>
<span id="cb5-19"><a href="#cb5-19"></a> <span class="cf">return</span> repeats_so_far</span></code></pre></div>
<p>Unfortunately, if we attempt to run our doctest examples above, we dont get the expected values. Instead, we get 3 <code>IndexError</code>s, one for each example. Here is the error for the first failed example:</p>
<pre><code>Failed example:
count_adjacent_repeats(&#39;look&#39;)
Exception raised:
Traceback (most recent call last):
File &quot;path\to\Python\Python38\lib\doctest.py&quot;, line 1329, in __run
exec(compile(example.source, filename, &quot;single&quot;,
File &quot;&lt;doctest __main__.count_adjacent_repeats[0]&gt;&quot;, line 1, in &lt;module&gt;
count_adjacent_repeats(&#39;look&#39;)
File &quot;path/to/functions.py&quot;, line 74, in count_adjacent_repeats
if string[i] == string[i + 1]:
IndexError: string index out of range</code></pre>
<p>Conveniently, the error tells us what the problem is (<code>'string index out of range'</code>). It even tells us the line where the error occurs: <code>if string[i] == string[i + 1]:</code>. It is now our job to figure out why the line is causing an <code>IndexError</code>. The line indexes the parameter <code>string</code> using <code>i</code> and <code>i + 1</code>, so one of them must be causing the error.</p>
<p>Remember that given a string of length <code>n</code>, the valid indices are from <code>0</code> to <code>n - 1</code>. Now lets look at our use of <code>range</code>: <code>for i in range(0, len(string))</code>. This means that <code>i</code> can take on the values <code>0</code> to <code>n - 1</code>, which seems to be in the correct bounds. But dont forget, we also are indexing using <code>i + 1</code>! This is the problem: <code>i + 1</code> can take on the values <code>1</code> to <code>n</code>, and <code>n</code> is not a valid index.</p>
<p>We can solve this bug by remembering our goal: to compare adjacent pairs of characters. For a string of length <code>n</code>, the last pair of characters is <code>(string[n - 2], string[n - 1])</code>, so our loop variable <code>i</code> only needs to go up to <code>n - 2</code>, not <code>n - 1</code>. Lets look at the final solution:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">def</span> count_adjacent_repeats(string: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb7-2"><a href="#cb7-2"></a> <span class="co">&quot;&quot;&quot;Return the number of repeated adjacent characters in string.</span></span>
<span id="cb7-3"><a href="#cb7-3"></a></span>
<span id="cb7-4"><a href="#cb7-4"></a><span class="co"> &gt;&gt;&gt; count_adjacent_repeats(&#39;look&#39;)</span></span>
<span id="cb7-5"><a href="#cb7-5"></a><span class="co"> 1</span></span>
<span id="cb7-6"><a href="#cb7-6"></a><span class="co"> &gt;&gt;&gt; count_adjacent_repeats(&#39;David&#39;)</span></span>
<span id="cb7-7"><a href="#cb7-7"></a><span class="co"> 0</span></span>
<span id="cb7-8"><a href="#cb7-8"></a><span class="co"> &gt;&gt;&gt; count_adjacent_repeats(&#39;canal&#39;)</span></span>
<span id="cb7-9"><a href="#cb7-9"></a><span class="co"> 0</span></span>
<span id="cb7-10"><a href="#cb7-10"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb7-11"><a href="#cb7-11"></a> <span class="co"># ACCUMULATOR repeats_so_far: keep track of the number of adjacent</span></span>
<span id="cb7-12"><a href="#cb7-12"></a> <span class="co"># characters that are identical</span></span>
<span id="cb7-13"><a href="#cb7-13"></a> repeats_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb7-14"><a href="#cb7-14"></a></span>
<span id="cb7-15"><a href="#cb7-15"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">0</span>, <span class="bu">len</span>(string) <span class="op">-</span> <span class="dv">1</span>):</span>
<span id="cb7-16"><a href="#cb7-16"></a> <span class="cf">if</span> string[i] <span class="op">==</span> string[i <span class="op">+</span> <span class="dv">1</span>]:</span>
<span id="cb7-17"><a href="#cb7-17"></a> repeats_so_far <span class="op">=</span> repeats_so_far <span class="op">+</span> <span class="dv">1</span></span>
<span id="cb7-18"><a href="#cb7-18"></a></span>
<span id="cb7-19"><a href="#cb7-19"></a> <span class="cf">return</span> repeats_so_far</span></code></pre></div>
<p>Notice that we could not have implemented this function using an element-based for loop. Having <code>for char in string</code> would let us access the current character (<code>char</code>), but <em>not</em> the next character adjacent to <code>char</code>. To summarize, when we want to write a loop body that compares the current element with another based on their positions, we must use an index-based loop to keep track of the current index in the loop.</p>
<h2 id="two-lists-one-loop">Two lists, one loop</h2>
<p>Index-based for loops can also be used to iterate over two collections in parallel using a single for loop. Consider the common mathematical problem: sum of products.<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote"> In your linear algebra course youll learn about the <em>inner product</em> operation, which formalizes this idea.</span></p>
<p>For example, suppose we have two nickels, four dimes, and three quarters in our pocket. How much money do we have in total? To solve this, we must know the value of nickels, dimes, and quarters. Then we can use sum of products:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1"></a><span class="op">&gt;&gt;&gt;</span> money_so_far <span class="op">=</span> <span class="fl">0.0</span></span>
<span id="cb8-2"><a href="#cb8-2"></a><span class="op">&gt;&gt;&gt;</span> money_so_far <span class="op">=</span> money_so_far <span class="op">+</span> <span class="dv">2</span> <span class="op">*</span> <span class="fl">0.05</span> <span class="co"># Two nickels</span></span>
<span id="cb8-3"><a href="#cb8-3"></a><span class="op">&gt;&gt;&gt;</span> money_so_far <span class="op">=</span> money_so_far <span class="op">+</span> <span class="dv">4</span> <span class="op">*</span> <span class="fl">0.10</span> <span class="co"># Four dimes</span></span>
<span id="cb8-4"><a href="#cb8-4"></a><span class="op">&gt;&gt;&gt;</span> money_so_far <span class="op">=</span> money_so_far <span class="op">+</span> <span class="dv">3</span> <span class="op">*</span> <span class="fl">0.25</span> <span class="co"># Three quarters</span></span>
<span id="cb8-5"><a href="#cb8-5"></a><span class="op">&gt;&gt;&gt;</span> money_so_far</span>
<span id="cb8-6"><a href="#cb8-6"></a><span class="fl">1.25</span></span></code></pre></div>
<p>This looks very similar to our <code>sum_so_far</code> exploration from earlier. The main difference is that this time we are accumulating products using the <code>*</code> operator. To the left of the <code>*</code> operator, we have a count (e.g., the number of nickels, an <code>int</code>). To the right of the <code>*</code> operator, we have a cent value (e.g., how much a nickel is worth in cents, a <code>float</code>). We can store this information in two same-sized lists. Lets design a function that uses these two lists to tell us how much money we have:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1"></a><span class="kw">def</span> count_money(counts: <span class="bu">list</span>[<span class="bu">int</span>], denoms: <span class="bu">list</span>[<span class="bu">float</span>]) <span class="op">-&gt;</span> <span class="bu">float</span>:</span>
<span id="cb9-2"><a href="#cb9-2"></a> <span class="co">&quot;&quot;&quot;Return the total amount of money for the given coin counts and denominations.</span></span>
<span id="cb9-3"><a href="#cb9-3"></a></span>
<span id="cb9-4"><a href="#cb9-4"></a><span class="co"> counts stores the number of coins of each type, and denominations stores the</span></span>
<span id="cb9-5"><a href="#cb9-5"></a><span class="co"> value of each coin type. Each element in counts corresponds to the element at</span></span>
<span id="cb9-6"><a href="#cb9-6"></a><span class="co"> the same index in denoms.</span></span>
<span id="cb9-7"><a href="#cb9-7"></a></span>
<span id="cb9-8"><a href="#cb9-8"></a><span class="co"> Preconditions:</span></span>
<span id="cb9-9"><a href="#cb9-9"></a><span class="co"> - len(counts) == len(values)</span></span>
<span id="cb9-10"><a href="#cb9-10"></a></span>
<span id="cb9-11"><a href="#cb9-11"></a><span class="co"> &gt;&gt;&gt; count_money([2, 4, 3], [0.05, 0.10, 0.25])</span></span>
<span id="cb9-12"><a href="#cb9-12"></a><span class="co"> 1.25</span></span>
<span id="cb9-13"><a href="#cb9-13"></a><span class="co"> &quot;&quot;&quot;</span></span></code></pre></div>
<p>Before using a loop, lets investigate how we would implement this using a comprehension. We need to multiply each corresponding element of <code>counts</code> and <code>denoms</code>, and add the results:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1"></a>(counts[<span class="dv">0</span>] <span class="op">*</span> denoms[<span class="dv">0</span>]) <span class="op">+</span> (counts[<span class="dv">1</span>] <span class="op">*</span> denoms[<span class="dv">1</span>]) <span class="op">+</span> (counts[<span class="dv">2</span>] <span class="op">*</span> denoms[<span class="dv">2</span>]) <span class="op">+</span> ...</span></code></pre></div>
<p>We can generate each of these products by using <code>range</code>:<label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote"> We used <code>len(counts)</code>, but could have used <code>len(denoms)</code> as well because of the functions precondition.</span></p>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1"></a>[counts[i] <span class="op">*</span> denoms[i] <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">0</span>, <span class="bu">len</span>(counts))]</span></code></pre></div>
<p>And we can then compute the sum of this expression by using the builtin Python function:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1"></a><span class="kw">def</span> count_money(counts: <span class="bu">list</span>[<span class="bu">int</span>], denoms: <span class="bu">list</span>[<span class="bu">float</span>]) <span class="op">-&gt;</span> <span class="bu">float</span>:</span>
<span id="cb12-2"><a href="#cb12-2"></a> <span class="co">&quot;&quot;&quot;Return the total amount of money for the given coin counts and denominations.</span></span>
<span id="cb12-3"><a href="#cb12-3"></a></span>
<span id="cb12-4"><a href="#cb12-4"></a><span class="co"> counts stores the number of coins of each type, and denominations stores the</span></span>
<span id="cb12-5"><a href="#cb12-5"></a><span class="co"> value of each coin type. Each element in counts corresponds to the element at</span></span>
<span id="cb12-6"><a href="#cb12-6"></a><span class="co"> the same index in denoms.</span></span>
<span id="cb12-7"><a href="#cb12-7"></a></span>
<span id="cb12-8"><a href="#cb12-8"></a><span class="co"> Preconditions:</span></span>
<span id="cb12-9"><a href="#cb12-9"></a><span class="co"> - len(counts) == len(values)</span></span>
<span id="cb12-10"><a href="#cb12-10"></a></span>
<span id="cb12-11"><a href="#cb12-11"></a><span class="co"> &gt;&gt;&gt; count_money([2, 4, 3], [0.05, 0.10, 0.25])</span></span>
<span id="cb12-12"><a href="#cb12-12"></a><span class="co"> 1.25</span></span>
<span id="cb12-13"><a href="#cb12-13"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb12-14"><a href="#cb12-14"></a> <span class="cf">return</span> <span class="bu">sum</span>([counts[i] <span class="op">*</span> denoms[i] <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">0</span>, <span class="bu">len</span>(counts))])</span></code></pre></div>
<p>This implementation of <code>count_money</code> has all the necessary ingredients that would appear in an equivalent for loop. Here is our alternate implementation of <code>count_money</code> using a for loop, but the same structure as <code>my_sum</code> from <a href="04-for-loops.html">4.4 Repeated Execution: For Loops</a>.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1"></a><span class="kw">def</span> count_money(counts: <span class="bu">list</span>[<span class="bu">int</span>], values: <span class="bu">list</span>[<span class="bu">float</span>]) <span class="op">-&gt;</span> <span class="bu">float</span>:</span>
<span id="cb13-2"><a href="#cb13-2"></a> <span class="co">&quot;&quot;&quot;...</span></span>
<span id="cb13-3"><a href="#cb13-3"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb13-4"><a href="#cb13-4"></a> <span class="co"># ACCUMULATOR money_so_far: keep track of the total money so far.</span></span>
<span id="cb13-5"><a href="#cb13-5"></a> money_so_far <span class="op">=</span> <span class="fl">0.0</span></span>
<span id="cb13-6"><a href="#cb13-6"></a></span>
<span id="cb13-7"><a href="#cb13-7"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">0</span>, <span class="bu">len</span>(counts)):</span>
<span id="cb13-8"><a href="#cb13-8"></a> money_so_far <span class="op">=</span> money_so_far <span class="op">+</span> counts[i] <span class="op">*</span> values[i]</span>
<span id="cb13-9"><a href="#cb13-9"></a></span>
<span id="cb13-10"><a href="#cb13-10"></a> <span class="cf">return</span> money_so_far</span></code></pre></div>
<h2 id="choosing-the-right-for-loop">Choosing the right for loop</h2>
<p>We have seen two forms of for loops. The first version, the element-based for loop, takes the form <code>for &lt;loop_variable&gt; in &lt;collection&gt;</code>. This is useful when we want to process each element in the collection without knowing about its position in the collection. The second version, the index-based for loops, takes the form <code>for &lt;loop_variable&gt; in &lt;range&gt;</code>. In index-based for loops, the <code>range</code> must belong to the set of valid indices for the collection we wish to loop over. We have seen two situations where this is useful:<label for="sn-3" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-3" class="margin-toggle"/><span class="sidenote"> Well see one more example use of index-based loops later this chapter.</span></p>
<ol type="1">
<li>When the location of elements in the collection matters (as in <code>count_adjacent_repeats</code>).</li>
<li>When we want to loop through more than one list at a time (as in <code>count_money</code>), using the same index for both lists.</li>
</ol>
<p>You might have noticed from our <code>my_sum</code> example that index-based for loops are <em>more powerful</em> than element-based for loops: given the current index, we can always access the current collection element, but not vice versa. So why dont we just always use index-based for loops? Two reasons: first, not all collections can be indexed (think <code>set</code> and <code>dict</code>); and second, index-based for loops introduce a level of <em>indirection</em> to our code. In our <code>my_sum_v2</code> example, we had to access the current element using list indexing (<code>numbers[i]</code>), while in <code>my_sum</code>, we could directly access the element by using the loop variable (<code>number</code>)`. So its important to understand when we can use element-based for loops vs. index-based for loops, as the former makes our code easier to write and understand.</p>
<h1 id="references">References</h1>
<ul>
<li>CSC108 videos: For loops over indices (<a href="https://youtu.be/tLnWFnnZ6sA">Part 1</a> only)</li>
<li>CSC108 videos: Parallel Lists and Strings (<a href="https://youtu.be/RpWIaXNiob0">Part 1</a>, <a href="https://youtu.be/t7RWk6VygwE">Part 2</a>)</li>
</ul>
</section>
<footer>
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
</footer>
</body>
</html>
+699
View File
@@ -0,0 +1,699 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>4.7 Nested For Loops</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="../tufte.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<div style="display:none">
\(
\newcommand{\NOT}{\neg}
\newcommand{\AND}{\wedge}
\newcommand{\OR}{\vee}
\newcommand{\XOR}{\oplus}
\newcommand{\IMP}{\Rightarrow}
\newcommand{\IFF}{\Leftrightarrow}
\newcommand{\TRUE}{\text{True}\xspace}
\newcommand{\FALSE}{\text{False}\xspace}
\newcommand{\IN}{\,{\in}\,}
\newcommand{\NOTIN}{\,{\notin}\,}
\newcommand{\TO}{\rightarrow}
\newcommand{\DIV}{\mid}
\newcommand{\NDIV}{\nmid}
\newcommand{\MOD}[1]{\pmod{#1}}
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
\newcommand{\N}{\mathbb N}
\newcommand{\Z}{\mathbb Z}
\newcommand{\Q}{\mathbb Q}
\newcommand{\R}{\mathbb R}
\newcommand{\C}{\mathbb C}
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
\newcommand{\cD}{\mathcal D}
\newcommand{\cE}{\mathcal E}
\newcommand{\cF}{\mathcal F}
\newcommand{\cG}{\mathcal G}
\newcommand{\cH}{\mathcal H}
\newcommand{\cI}{\mathcal I}
\newcommand{\cJ}{\mathcal J}
\newcommand{\cL}{\mathcal L}
\newcommand{\cK}{\mathcal K}
\newcommand{\cN}{\mathcal N}
\newcommand{\cO}{\mathcal O}
\newcommand{\cP}{\mathcal P}
\newcommand{\cQ}{\mathcal Q}
\newcommand{\cS}{\mathcal S}
\newcommand{\cT}{\mathcal T}
\newcommand{\cV}{\mathcal V}
\newcommand{\cW}{\mathcal W}
\newcommand{\cZ}{\mathcal Z}
\newcommand{\emp}{\emptyset}
\newcommand{\bs}{\backslash}
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
\newcommand{\abs}[1]{\left | #1 \right |}
\newcommand{\xspace}{}
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
\)
</div>
<header id="title-block-header">
<h1 class="title">4.7 Nested For Loops</h1>
</header>
<section>
<p>When we introduced for loops, we said that the loop body consists of one of more statements. We saw in <a href="05-more-for-loops.html">4.5 For Loop Variations</a> that we could put if statements inside loop bodies. In this section, well see that a for loop body can itself contain another for loop, since for loops are themselves statements. Well study uses of these <em>nested for loops</em>, and also draw comparisons between them and comprehensions from the previous chapter.</p>
<h2 id="nested-loops-and-nested-data">Nested loops and nested data</h2>
<p>Nested loops are particularly useful when dealing with nested data. As a first example, suppose we have a list of lists of integers:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="op">&gt;&gt;&gt;</span> lists_of_numbers <span class="op">=</span> [[<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>], [<span class="dv">10</span>, <span class="op">-</span><span class="dv">5</span>], [<span class="dv">100</span>]]</span></code></pre></div>
<p>Our goal is to compute the sum of all of the elements of this list:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">def</span> sum_all(lists_of_numbers: <span class="bu">list</span>[<span class="bu">list</span>[<span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb2-2"><a href="#cb2-2"></a> <span class="co">&quot;&quot;&quot;Return the sum of all the numbers in the given lists_of_numbers.</span></span>
<span id="cb2-3"><a href="#cb2-3"></a></span>
<span id="cb2-4"><a href="#cb2-4"></a><span class="co"> &gt;&gt;&gt; sum_all([[1, 2, 3], [10, -5], [100]])</span></span>
<span id="cb2-5"><a href="#cb2-5"></a><span class="co"> 111</span></span>
<span id="cb2-6"><a href="#cb2-6"></a><span class="co"> &quot;&quot;&quot;</span></span></code></pre></div>
<p>We can start with our basic loop accumulator pattern:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">def</span> sum_all(lists_of_numbers: <span class="bu">list</span>[<span class="bu">list</span>[<span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb3-2"><a href="#cb3-2"></a> <span class="co">&quot;&quot;&quot;...&quot;&quot;&quot;</span></span>
<span id="cb3-3"><a href="#cb3-3"></a> <span class="co"># ACCUMULATOR sum_so_far: keep track of the running sum of the numbers.</span></span>
<span id="cb3-4"><a href="#cb3-4"></a> sum_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb3-5"><a href="#cb3-5"></a></span>
<span id="cb3-6"><a href="#cb3-6"></a> <span class="cf">for</span> ... <span class="kw">in</span> lists_of_numbers:</span>
<span id="cb3-7"><a href="#cb3-7"></a> sum_so_far <span class="op">=</span> ...</span>
<span id="cb3-8"><a href="#cb3-8"></a></span>
<span id="cb3-9"><a href="#cb3-9"></a> <span class="cf">return</span> sum_so_far</span></code></pre></div>
<p>The difference between this function and in <a href="04-for-loops.html"><code>my_sum</code> from 4.4</a> is that here our loop variable in <code>for ... in lists_of_numbers</code> does not refer to a single number, but rather a list of numbers:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">def</span> sum_all(lists_of_numbers: <span class="bu">list</span>[<span class="bu">list</span>[<span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb4-2"><a href="#cb4-2"></a> <span class="co">&quot;&quot;&quot;...&quot;&quot;&quot;</span></span>
<span id="cb4-3"><a href="#cb4-3"></a> <span class="co"># ACCUMULATOR sum_so_far: keep track of the running sum of the numbers.</span></span>
<span id="cb4-4"><a href="#cb4-4"></a> sum_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb4-5"><a href="#cb4-5"></a></span>
<span id="cb4-6"><a href="#cb4-6"></a> <span class="cf">for</span> numbers <span class="kw">in</span> lists_of_numbers: <span class="co"># numbers is a list of numbers, not a single number!</span></span>
<span id="cb4-7"><a href="#cb4-7"></a> sum_so_far <span class="op">=</span> ...</span>
<span id="cb4-8"><a href="#cb4-8"></a></span>
<span id="cb4-9"><a href="#cb4-9"></a> <span class="cf">return</span> sum_so_far</span></code></pre></div>
<p>So here is one way of completing this function, by using the builtin <code>sum</code> function:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw">def</span> sum_all(lists_of_numbers: <span class="bu">list</span>[<span class="bu">list</span>[<span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb5-2"><a href="#cb5-2"></a> <span class="co">&quot;&quot;&quot;...&quot;&quot;&quot;</span></span>
<span id="cb5-3"><a href="#cb5-3"></a> <span class="co"># ACCUMULATOR sum_so_far: keep track of the running sum of the numbers.</span></span>
<span id="cb5-4"><a href="#cb5-4"></a> sum_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb5-5"><a href="#cb5-5"></a></span>
<span id="cb5-6"><a href="#cb5-6"></a> <span class="cf">for</span> numbers <span class="kw">in</span> lists_of_numbers: <span class="co"># numbers is a list of numbers, not a single number!</span></span>
<span id="cb5-7"><a href="#cb5-7"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> <span class="bu">sum</span>(numbers)</span>
<span id="cb5-8"><a href="#cb5-8"></a></span>
<span id="cb5-9"><a href="#cb5-9"></a> <span class="cf">return</span> sum_so_far</span></code></pre></div>
<p>This implementation is structurally similar to the <code>my_sum</code> implementation we had in Section 4.4. But how would we implement this function <em>without</em> using <code>sum</code>? For this we need another for loop:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="kw">def</span> sum_all(lists_of_numbers: <span class="bu">list</span>[<span class="bu">list</span>[<span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb6-2"><a href="#cb6-2"></a> <span class="co">&quot;&quot;&quot;...&quot;&quot;&quot;</span></span>
<span id="cb6-3"><a href="#cb6-3"></a> <span class="co"># ACCUMULATOR sum_so_far: keep track of the running sum of the numbers.</span></span>
<span id="cb6-4"><a href="#cb6-4"></a> sum_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb6-5"><a href="#cb6-5"></a></span>
<span id="cb6-6"><a href="#cb6-6"></a> <span class="cf">for</span> numbers <span class="kw">in</span> lists_of_numbers: <span class="co"># numbers is a list of numbers, not a single number!</span></span>
<span id="cb6-7"><a href="#cb6-7"></a> <span class="cf">for</span> number <span class="kw">in</span> numbers: <span class="co"># number is a single number</span></span>
<span id="cb6-8"><a href="#cb6-8"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> number</span>
<span id="cb6-9"><a href="#cb6-9"></a></span>
<span id="cb6-10"><a href="#cb6-10"></a> <span class="cf">return</span> sum_so_far</span></code></pre></div>
<p>We say that the <code>for number in numbers</code> loops is <em>nested</em> within the <code>for numbers in lists_of_numbers</code>. What happens when we call our doctest example, <code>sum_all([[1, 2, 3], [10, -5], [100]])</code>? Lets break this down step by step.</p>
<ol type="1">
<li><p>First, the assignment statement <code>sum_so_far = 0</code> executes, creating our accumulator variable.</p></li>
<li><p>The outer loop is reached.</p>
<ul>
<li><p>The loop variable <code>list_of_numbers</code> is assigned the first element in <code>lists_of_numbers</code>, which is <code>[1, 2, 3]</code>.</p></li>
<li><p>Then, the body of the outer loop is executed. Its body is just one statement: the inner for loop, <code>for number in numbers</code>.</p>
<ul>
<li><p>The inner loop variable <code>number</code> is assigned the first value in <code>numbers</code>, which is <code>1</code>.</p></li>
<li><p>The inner loop body gets executed, updating the accumulator. <code>sum_so_far</code> is reassigned to <code>1</code> (since <code>0 + 1 == 1</code>).</p></li>
<li><p>The inner loop iterates twice more, for <code>number = 2</code> and <code>number = 3</code>.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> Notice that <code>numbers</code> is the *same value (<code>[1, 2, 3]</code>) for this entire part. </span> At each iteration, the accumulator is updated, first by adding <code>2</code> and then <code>3</code>. At this point, <code>sum_so_far = 6</code> (<code>0 + 1 + 2 + 3</code>).</p></li>
<li><p>After all three iterations of the inner loop occur, the inner loop stops. The Python interpreter is done executing this statement.</p></li>
</ul></li>
<li><p>The next iteration of the <em>outer loop</em> occurs; <code>numbers</code> is assigned to the list <code>[10, -5]</code>.</p></li>
<li><p>Again, the body of the outer loop occurs.</p>
<ul>
<li>The inner loop now iterates twice: for <code>number = 10</code> and <code>number = -5</code>. <code>sum_so_far</code> is reassigned twice more, with a final value of <code>11</code> (<code>6 + 10 + -5</code>).</li>
</ul></li>
<li><p>The outer loop iterates one more time, for <code>numbers = [100]</code>.</p></li>
<li><p>Again, the body of the outer loop occurs.</p>
<ul>
<li>The inner loop iterates once, for <code>number = 100</code>. <code>sum_so_far</code> is reassigned to <code>111</code> (<code>11 + 100</code>).</li>
</ul></li>
<li><p>At last, there are no more iterations of the outer loop, and so it stops.</p></li>
</ul></li>
<li><p>After the outer loop is done, the <code>return</code> statement executes, returning the value of <code>sum_so_far</code>, which is <code>111</code>.</p></li>
</ol>
<p>Whew, thats a lot of writing! We can summarize the above behaviour by creating a <em>loop accumulation table</em>. Note that the table below has the same structure as the ones weve seen before, but is more complex because its columns include both the outer and inner loop variables and iterations. The <code>accumulator</code> column shows the value of <code>sum_so_far</code> at the <em>end</em> of the iteration of the inner loop. Pay close attention to the <em>order</em> of the rows, as this matches the order of execution we described above.</p>
<div class="fullwidth reference-table">
<table>
<colgroup>
<col style="width: 21%" />
<col style="width: 20%" />
<col style="width: 21%" />
<col style="width: 20%" />
<col style="width: 15%" />
</colgroup>
<thead>
<tr class="header">
<th>Outer loop iteration</th>
<th>Outer loop variable (<code>list_of_numbers</code>)</th>
<th>Inner loop iteration</th>
<th>Inner loop variable (<code>number</code>)</th>
<th>Accumulator (<code>sum_so_far</code>)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>0</td>
<td></td>
<td></td>
<td></td>
<td><code>0</code></td>
</tr>
<tr class="even">
<td>1</td>
<td><code>[1, 2, 3]</code></td>
<td>0</td>
<td></td>
<td><code>0</code></td>
</tr>
<tr class="odd">
<td>1</td>
<td><code>[1, 2, 3]</code></td>
<td>1</td>
<td><code>1</code></td>
<td><code>1</code></td>
</tr>
<tr class="even">
<td>1</td>
<td><code>[1, 2, 3]</code></td>
<td>2</td>
<td><code>2</code></td>
<td><code>3</code></td>
</tr>
<tr class="odd">
<td>1</td>
<td><code>[1, 2, 3]</code></td>
<td>3</td>
<td><code>3</code></td>
<td><code>6</code></td>
</tr>
<tr class="even">
<td>2</td>
<td><code>[10, -5]</code></td>
<td>0</td>
<td></td>
<td><code>6</code></td>
</tr>
<tr class="odd">
<td>2</td>
<td><code>[10, -5]</code></td>
<td>1</td>
<td><code>10</code></td>
<td><code>16</code></td>
</tr>
<tr class="even">
<td>2</td>
<td><code>[10, -5]</code></td>
<td>2</td>
<td><code>-5</code></td>
<td><code>11</code></td>
</tr>
<tr class="odd">
<td>3</td>
<td><code>[100]</code></td>
<td>0</td>
<td></td>
<td><code>11</code></td>
</tr>
<tr class="even">
<td>3</td>
<td><code>[100]</code></td>
<td>1</td>
<td><code>100</code></td>
<td><code>111</code></td>
</tr>
</tbody>
</table>
</div>
<h2 id="the-cartesian-product">The Cartesian product</h2>
<p>Our next example illustrates how to use nested loops on two different collections, obtaining all pairs of possible values from each collection. If that sounds familiar, well, it should be!</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">def</span> product(set1: <span class="bu">set</span>, set2: <span class="bu">set</span>) <span class="op">-&gt;</span> <span class="bu">set</span>[<span class="bu">tuple</span>]:</span>
<span id="cb7-2"><a href="#cb7-2"></a> <span class="co">&quot;&quot;&quot;Return the Cartesian product of set1 and set2.</span></span>
<span id="cb7-3"><a href="#cb7-3"></a></span>
<span id="cb7-4"><a href="#cb7-4"></a><span class="co"> &gt;&gt;&gt; result = product({10, 11}, {5, 6, 7})</span></span>
<span id="cb7-5"><a href="#cb7-5"></a><span class="co"> &gt;&gt;&gt; result == {(10, 5), (10, 6), (10, 7), (11, 5), (11, 6), (11, 7)}</span></span>
<span id="cb7-6"><a href="#cb7-6"></a><span class="co"> True</span></span>
<span id="cb7-7"><a href="#cb7-7"></a><span class="co"> &quot;&quot;&quot;</span></span></code></pre></div>
<p>Before we get to writing any loops at all, lets remind ourselves how we would write a comprehension to compute the Cartesian product:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1"></a><span class="op">&gt;&gt;&gt;</span> set1 <span class="op">=</span> {<span class="dv">10</span>, <span class="dv">11</span>}</span>
<span id="cb8-2"><a href="#cb8-2"></a><span class="op">&gt;&gt;&gt;</span> set2 <span class="op">=</span> {<span class="dv">5</span>, <span class="dv">6</span>, <span class="dv">7</span>}</span>
<span id="cb8-3"><a href="#cb8-3"></a><span class="op">&gt;&gt;&gt;</span> result <span class="op">=</span> {(x, y) <span class="cf">for</span> x <span class="kw">in</span> set1 <span class="cf">for</span> y <span class="kw">in</span> set2}</span>
<span id="cb8-4"><a href="#cb8-4"></a><span class="op">&gt;&gt;&gt;</span> result <span class="op">==</span> {(<span class="dv">10</span>, <span class="dv">5</span>), (<span class="dv">10</span>, <span class="dv">6</span>), (<span class="dv">10</span>, <span class="dv">7</span>), (<span class="dv">11</span>, <span class="dv">5</span>), (<span class="dv">11</span>, <span class="dv">6</span>), (<span class="dv">11</span>, <span class="dv">7</span>)}</span>
<span id="cb8-5"><a href="#cb8-5"></a><span class="va">True</span></span></code></pre></div>
<p>Now well see how to write this using nested for loop:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1"></a><span class="kw">def</span> cartesian_product(set1: <span class="bu">set</span>, set2: <span class="bu">set</span>) <span class="op">-&gt;</span> <span class="bu">set</span>[<span class="bu">tuple</span>]:</span>
<span id="cb9-2"><a href="#cb9-2"></a> <span class="co">&quot;&quot;&quot;Return the Cartesian product of set1 and set2.</span></span>
<span id="cb9-3"><a href="#cb9-3"></a></span>
<span id="cb9-4"><a href="#cb9-4"></a><span class="co"> &gt;&gt;&gt; result = cartesian_product({10, 11}, {5, 6, 7})</span></span>
<span id="cb9-5"><a href="#cb9-5"></a><span class="co"> &gt;&gt;&gt; result == {(10, 5), (10, 6), (10, 7), (11, 5), (11, 6), (11, 7)}</span></span>
<span id="cb9-6"><a href="#cb9-6"></a><span class="co"> True</span></span>
<span id="cb9-7"><a href="#cb9-7"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb9-8"><a href="#cb9-8"></a> <span class="co"># ACCUMULATOR product_so_far: keep track of the tuples from the pairs</span></span>
<span id="cb9-9"><a href="#cb9-9"></a> <span class="co"># of elements visited so far.</span></span>
<span id="cb9-10"><a href="#cb9-10"></a> product_so_far <span class="op">=</span> <span class="bu">set</span>()</span>
<span id="cb9-11"><a href="#cb9-11"></a></span>
<span id="cb9-12"><a href="#cb9-12"></a> <span class="cf">for</span> x <span class="kw">in</span> set1:</span>
<span id="cb9-13"><a href="#cb9-13"></a> <span class="cf">for</span> y <span class="kw">in</span> set2:</span>
<span id="cb9-14"><a href="#cb9-14"></a> product_so_far <span class="op">=</span> <span class="bu">set</span>.union(product_so_far, {(x, y)})</span>
<span id="cb9-15"><a href="#cb9-15"></a></span>
<span id="cb9-16"><a href="#cb9-16"></a> <span class="cf">return</span> product_so_far</span></code></pre></div>
<p>As we saw in our first example, here the inner loop <code>for y in set2</code> iterates through every element of <code>set2</code> for every element of <code>x</code> in <code>set1</code>. You can visualize this in the following loop accumulation table:</p>
<div class="fullwidth reference-table">
<table>
<colgroup>
<col style="width: 16%" />
<col style="width: 12%" />
<col style="width: 16%" />
<col style="width: 12%" />
<col style="width: 42%" />
</colgroup>
<thead>
<tr class="header">
<th>Outer loop iteration</th>
<th>Outer loop var (<code>x</code>)</th>
<th>Inner loop iteration</th>
<th>Inner loop var (<code>y</code>)</th>
<th>Accumulator (<code>product_so_far</code>)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>0</td>
<td></td>
<td></td>
<td></td>
<td><code>set()</code></td>
</tr>
<tr class="even">
<td>1</td>
<td><code>10</code></td>
<td>0</td>
<td></td>
<td><code>set()</code></td>
</tr>
<tr class="odd">
<td>1</td>
<td><code>10</code></td>
<td>1</td>
<td><code>5</code></td>
<td><code>{(10, 5)}</code></td>
</tr>
<tr class="even">
<td>1</td>
<td><code>10</code></td>
<td>2</td>
<td><code>6</code></td>
<td><code>{(10, 5), (10, 6)}</code></td>
</tr>
<tr class="odd">
<td>1</td>
<td><code>10</code></td>
<td>3</td>
<td><code>7</code></td>
<td><code>{(10, 5), (10, 6), (10, 7)}</code></td>
</tr>
<tr class="even">
<td>2</td>
<td><code>11</code></td>
<td>0</td>
<td></td>
<td><code>{(10, 5), (10, 6), (10, 7)}</code></td>
</tr>
<tr class="odd">
<td>2</td>
<td><code>11</code></td>
<td>1</td>
<td><code>5</code></td>
<td><code>{(10, 5), (10, 6), (10, 7), (11, 5)}</code></td>
</tr>
<tr class="even">
<td>2</td>
<td><code>11</code></td>
<td>2</td>
<td><code>6</code></td>
<td><code>{(10, 5), (10, 6), (10, 7), (11, 5), (11, 6)}</code></td>
</tr>
<tr class="odd">
<td>2</td>
<td><code>11</code></td>
<td>3</td>
<td><code>7</code></td>
<td><code>{(10, 5), (10, 6), (10, 7), (11, 5), (11, 6), (11, 7)}</code></td>
</tr>
</tbody>
</table>
</div>
<p>Another way of visualizing the return value is:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1"></a>{</span>
<span id="cb10-2"><a href="#cb10-2"></a> (<span class="dv">10</span>, <span class="dv">5</span>), (<span class="dv">10</span>, <span class="dv">6</span>), (<span class="dv">10</span>, <span class="dv">7</span>), <span class="co"># First three tuples are from the first iteration of the outer loop</span></span>
<span id="cb10-3"><a href="#cb10-3"></a> (<span class="dv">11</span>, <span class="dv">5</span>), (<span class="dv">11</span>, <span class="dv">6</span>), (<span class="dv">11</span>, <span class="dv">7</span>) <span class="co"># Next three tuples are from the second iteration of the outer loop</span></span>
<span id="cb10-4"><a href="#cb10-4"></a>}</span></code></pre></div>
<h2 id="outer-and-inner-accumulators">Outer and inner accumulators</h2>
<p>Both the <code>sum_all</code> and <code>cartesian_product</code> examples weve seen so far have used a single accumulator that is updated inside the inner loop body. However, <em>each loop</em> can have its own accumulator (and in fact, more than one accumulator). This is more complex, but offers more flexibilty than a single accumulator does alone.</p>
<p>As an example, suppose we have a list of lists of integers called <code>grades</code>. Each element of <code>grades</code> corresponds to a course and contains a list of grades obtained in that course. Lets see an example of the data:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1"></a><span class="op">&gt;&gt;&gt;</span> grades <span class="op">=</span> [</span>
<span id="cb11-2"><a href="#cb11-2"></a>... [<span class="dv">70</span>, <span class="dv">75</span>, <span class="dv">80</span>], <span class="co"># ENG196</span></span>
<span id="cb11-3"><a href="#cb11-3"></a>... [<span class="dv">70</span>, <span class="dv">80</span>, <span class="dv">90</span>, <span class="dv">100</span>], <span class="co"># CSC110</span></span>
<span id="cb11-4"><a href="#cb11-4"></a>... [<span class="dv">80</span>, <span class="dv">100</span>] <span class="co"># MAT137</span></span>
<span id="cb11-5"><a href="#cb11-5"></a>... ]</span></code></pre></div>
<p>Notice how the list of grades for course ENG196 does not have the same length as CSC110 or MAT137. Our goal is to return a new list containing the <em>average grade</em> of each course. We saw in Section 4.5 how to use loops to calculate the average of a collection of numbers:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1"></a><span class="kw">def</span> average(numbers: Iterable[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">float</span>:</span>
<span id="cb12-2"><a href="#cb12-2"></a> <span class="co">&quot;&quot;&quot;Return the average of a collection of integers.</span></span>
<span id="cb12-3"><a href="#cb12-3"></a></span>
<span id="cb12-4"><a href="#cb12-4"></a><span class="co"> Preconditions:</span></span>
<span id="cb12-5"><a href="#cb12-5"></a><span class="co"> - len(numbers) &gt; 0</span></span>
<span id="cb12-6"><a href="#cb12-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb12-7"><a href="#cb12-7"></a> <span class="co"># ACCUMULATOR len_so_far: keep track of the number of elements seen so far in the loop.</span></span>
<span id="cb12-8"><a href="#cb12-8"></a> len_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb12-9"><a href="#cb12-9"></a> <span class="co"># ACCUMULATOR total_so_far: keep track of the total of the elements seen so far in the loop.</span></span>
<span id="cb12-10"><a href="#cb12-10"></a> total_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb12-11"><a href="#cb12-11"></a></span>
<span id="cb12-12"><a href="#cb12-12"></a> <span class="cf">for</span> number <span class="kw">in</span> numbers:</span>
<span id="cb12-13"><a href="#cb12-13"></a> len_so_far <span class="op">=</span> len_so_far <span class="op">+</span> <span class="dv">1</span></span>
<span id="cb12-14"><a href="#cb12-14"></a> total_so_far <span class="op">=</span> total_so_far <span class="op">+</span> number</span>
<span id="cb12-15"><a href="#cb12-15"></a></span>
<span id="cb12-16"><a href="#cb12-16"></a> <span class="cf">return</span> total_so_far <span class="op">/</span> len_so_far</span></code></pre></div>
<p>We can calculate a list of averages for each course using a comprehension:<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote"> Exercise: write a precondition expression to guarantee there are no empty lists in <code>grades</code>.</span></p>
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1"></a><span class="kw">def</span> course_averages_v1(grades: <span class="bu">list</span>[<span class="bu">list</span>[<span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="bu">list</span>[<span class="bu">float</span>]:</span>
<span id="cb13-2"><a href="#cb13-2"></a> <span class="co">&quot;&quot;&quot;Return a new list for which each element is the average of the grades</span></span>
<span id="cb13-3"><a href="#cb13-3"></a><span class="co"> in the inner list at the corresponding position of grades.</span></span>
<span id="cb13-4"><a href="#cb13-4"></a></span>
<span id="cb13-5"><a href="#cb13-5"></a><span class="co"> &gt;&gt;&gt; course_averages_v1([[70, 75, 80], [70, 80, 90, 100], [80, 100]])</span></span>
<span id="cb13-6"><a href="#cb13-6"></a><span class="co"> [75.0, 85.0, 90.0]</span></span>
<span id="cb13-7"><a href="#cb13-7"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb13-8"><a href="#cb13-8"></a> <span class="cf">return</span> [average(course_grades) <span class="cf">for</span> course_grades <span class="kw">in</span> grades]</span></code></pre></div>
<p>We can translate this into a for loop using a list accumulator variable and list concatenation for the update:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1"></a><span class="kw">def</span> course_averages_v2(grades: <span class="bu">list</span>[<span class="bu">list</span>[<span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="bu">list</span>[<span class="bu">float</span>]:</span>
<span id="cb14-2"><a href="#cb14-2"></a> <span class="co">&quot;&quot;&quot;Return a new list for which each element is the average of the grades</span></span>
<span id="cb14-3"><a href="#cb14-3"></a><span class="co"> in the inner list at the corresponding position of grades.</span></span>
<span id="cb14-4"><a href="#cb14-4"></a></span>
<span id="cb14-5"><a href="#cb14-5"></a><span class="co"> &gt;&gt;&gt; course_averages_v2([[70, 75, 80], [70, 80, 90, 100], [80, 100]])</span></span>
<span id="cb14-6"><a href="#cb14-6"></a><span class="co"> [75.0, 85.0, 90.0]</span></span>
<span id="cb14-7"><a href="#cb14-7"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb14-8"><a href="#cb14-8"></a> <span class="co"># ACCUMULATOR averages_so_far: keep track of the averages of the lists</span></span>
<span id="cb14-9"><a href="#cb14-9"></a> <span class="co"># visited so far in grades.</span></span>
<span id="cb14-10"><a href="#cb14-10"></a> averages_so_far <span class="op">=</span> []</span>
<span id="cb14-11"><a href="#cb14-11"></a></span>
<span id="cb14-12"><a href="#cb14-12"></a> <span class="cf">for</span> course_grades <span class="kw">in</span> grades:</span>
<span id="cb14-13"><a href="#cb14-13"></a> course_average <span class="op">=</span> average(course_grades)</span>
<span id="cb14-14"><a href="#cb14-14"></a> averages_so_far <span class="op">=</span> averages_so_far <span class="op">+</span> [course_average]</span>
<span id="cb14-15"><a href="#cb14-15"></a></span>
<span id="cb14-16"><a href="#cb14-16"></a> <span class="cf">return</span> averages_so_far</span></code></pre></div>
<p>Now lets see how to calculate the <code>course_average</code> variable for each course by using an inner loop instead of the <code>average</code> function. We can do this by <em>expanding the definition of <code>average</code></em> directly in the loop body, with just a few minor tweaks:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1"></a><span class="kw">def</span> course_averages_v3(grades: <span class="bu">list</span>[<span class="bu">list</span>[<span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="bu">list</span>[<span class="bu">float</span>]:</span>
<span id="cb15-2"><a href="#cb15-2"></a> <span class="co">&quot;&quot;&quot;Return a new list for which each element is the average of the grades</span></span>
<span id="cb15-3"><a href="#cb15-3"></a><span class="co"> in the inner list at the corresponding position of grades.</span></span>
<span id="cb15-4"><a href="#cb15-4"></a></span>
<span id="cb15-5"><a href="#cb15-5"></a><span class="co"> &gt;&gt;&gt; course_averages_v3([[70, 75, 80], [70, 80, 90, 100], [80, 100]])</span></span>
<span id="cb15-6"><a href="#cb15-6"></a><span class="co"> [75.0, 85.0, 90.0]</span></span>
<span id="cb15-7"><a href="#cb15-7"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb15-8"><a href="#cb15-8"></a> <span class="co"># ACCUMULATOR averages_so_far: keep track of the averages of the lists</span></span>
<span id="cb15-9"><a href="#cb15-9"></a> <span class="co"># visited so far in grades.</span></span>
<span id="cb15-10"><a href="#cb15-10"></a> averages_so_far <span class="op">=</span> []</span>
<span id="cb15-11"><a href="#cb15-11"></a></span>
<span id="cb15-12"><a href="#cb15-12"></a> <span class="cf">for</span> course_grades <span class="kw">in</span> grades:</span>
<span id="cb15-13"><a href="#cb15-13"></a> <span class="co"># ACCUMULATOR len_so_far: keep track of the number of elements seen so far in course_grades.</span></span>
<span id="cb15-14"><a href="#cb15-14"></a> len_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb15-15"><a href="#cb15-15"></a> <span class="co"># ACCUMULATOR total_so_far: keep track of the total of the elements seen so far in course_grades.</span></span>
<span id="cb15-16"><a href="#cb15-16"></a> total_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb15-17"><a href="#cb15-17"></a></span>
<span id="cb15-18"><a href="#cb15-18"></a> <span class="cf">for</span> grade <span class="kw">in</span> course_grades:</span>
<span id="cb15-19"><a href="#cb15-19"></a> len_so_far <span class="op">=</span> len_so_far <span class="op">+</span> <span class="dv">1</span></span>
<span id="cb15-20"><a href="#cb15-20"></a> total_so_far <span class="op">=</span> total_so_far <span class="op">+</span> grade</span>
<span id="cb15-21"><a href="#cb15-21"></a></span>
<span id="cb15-22"><a href="#cb15-22"></a> course_average <span class="op">=</span> total_so_far <span class="op">/</span> len_so_far</span>
<span id="cb15-23"><a href="#cb15-23"></a></span>
<span id="cb15-24"><a href="#cb15-24"></a> averages_so_far <span class="op">=</span> averages_so_far <span class="op">+</span> [course_average]</span>
<span id="cb15-25"><a href="#cb15-25"></a></span>
<span id="cb15-26"><a href="#cb15-26"></a> <span class="cf">return</span> averages_so_far</span></code></pre></div>
<p>It may be surprising to you that we can do this! Just as how in the last chapter we saw that we can take a predicate and expand it into its definition, we can do the same thing for Python functions with multiple statements in their body. The only change we needed to make was the return statement of <code>average</code>. The original function had the statement <code>return total_so_far / len_so_far</code>. Because our loop assigned this return value to <code>course_average</code>, we changed the code to:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1"></a>course_average <span class="op">=</span> total_so_far <span class="op">/</span> len_so_far</span></code></pre></div>
<p>One important note about the structure of this nested loop is that the inner loop accumulators are assigned to <em>inside</em> the body of the outer loop*, rather than at the top of the function body. This is because the accumulators <code>len_so_far</code> and <code>total_so_far</code> are specific to <code>course_grades</code>, which changes at each iteration of the outer loop. The statements <code>len_so_far = 0</code> and <code>total_so_far = 0</code> act to “reset” these accumulators for each new <code>course_grades</code> list.</p>
<p>Lets take a look at our final loop accumulation table in this section, which illustrates the execution of <code>course_averages_v3([[70, 75, 80], [70, 80, 90, 100], [80, 100]])</code> and how each loop variable and accumulator changes. Please take your time studying this table carefully—it isnt designed to be a “quick read”, but to really deepen your understand of whats going on!</p>
<div class="fullwidth reference-table">
<table>
<colgroup>
<col style="width: 9%" />
<col style="width: 16%" />
<col style="width: 9%" />
<col style="width: 16%" />
<col style="width: 15%" />
<col style="width: 15%" />
<col style="width: 17%" />
</colgroup>
<thead>
<tr class="header">
<th>Outer loop iteration</th>
<th>Outer loop variable (<code>course_grades</code>)</th>
<th>Inner loop iteration</th>
<th>Inner loop variable (<code>grade</code>)</th>
<th>Inner accumulator (<code>len_so_far</code>)</th>
<th>Inner accumulator (<code>total_so_far</code>)</th>
<th>Outer accumulator (<code>averages_so_far</code>)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>0</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td><code>[]</code></td>
</tr>
<tr class="even">
<td>1</td>
<td><code>[70, 75, 80]</code></td>
<td>0</td>
<td></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>[]</code></td>
</tr>
<tr class="odd">
<td>1</td>
<td><code>[70, 75, 80]</code></td>
<td>1</td>
<td><code>70</code></td>
<td><code>1</code></td>
<td><code>70</code></td>
<td><code>[]</code></td>
</tr>
<tr class="even">
<td>1</td>
<td><code>[70, 75, 80]</code></td>
<td>2</td>
<td><code>75</code></td>
<td><code>2</code></td>
<td><code>145</code></td>
<td><code>[]</code></td>
</tr>
<tr class="odd">
<td>1</td>
<td><code>[70, 75, 80]</code></td>
<td>3</td>
<td><code>80</code></td>
<td><code>3</code></td>
<td><code>225</code></td>
<td><code>[75.0]</code></td>
</tr>
<tr class="even">
<td>2</td>
<td><code>[70, 80, 90, 100]</code></td>
<td>0</td>
<td></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>[75.0]</code></td>
</tr>
<tr class="odd">
<td>2</td>
<td><code>[70, 80, 90, 100]</code></td>
<td>1</td>
<td><code>70</code></td>
<td><code>1</code></td>
<td><code>70</code></td>
<td><code>[75.0]</code></td>
</tr>
<tr class="even">
<td>2</td>
<td><code>[70, 80, 90, 100]</code></td>
<td>2</td>
<td><code>80</code></td>
<td><code>2</code></td>
<td><code>150</code></td>
<td><code>[75.0]</code></td>
</tr>
<tr class="odd">
<td>2</td>
<td><code>[70, 80, 90, 100]</code></td>
<td>3</td>
<td><code>90</code></td>
<td><code>3</code></td>
<td><code>240</code></td>
<td><code>[75.0]</code></td>
</tr>
<tr class="even">
<td>2</td>
<td><code>[70, 80, 90, 100]</code></td>
<td>4</td>
<td><code>100</code></td>
<td><code>4</code></td>
<td><code>340</code></td>
<td><code>[75.0, 85.0]</code></td>
</tr>
<tr class="odd">
<td>3</td>
<td><code>[80, 100]</code></td>
<td>0</td>
<td></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>[75.0, 85.0]</code></td>
</tr>
<tr class="even">
<td>3</td>
<td><code>[80, 100]</code></td>
<td>1</td>
<td><code>80</code></td>
<td><code>1</code></td>
<td><code>80</code></td>
<td><code>[75.0, 85.0]</code></td>
</tr>
<tr class="odd">
<td>3</td>
<td><code>[80, 100]</code></td>
<td>2</td>
<td><code>100</code></td>
<td><code>2</code></td>
<td><code>180</code></td>
<td><code>[75.0, 85.0, 90.0]</code></td>
</tr>
</tbody>
</table>
</div>
<h2 id="summary-understanding-and-simplifying-nested-for-loops">Summary: understanding and simplifying nested for loops</h2>
<p>Nested for loops are a powerful tool in our understanding of the Python programming language, but they are by far the most complex and most error-prone that weve studied so far. Just as we saw with nested expressions and nested if statements, nested loops have the potential to greatly increase the size and complexity of our code. Contrast the implementation of <code>course_averages_v3</code> against <code>course_averages_v2</code> (or <code>course_averages_v1</code>), for example.</p>
<p>While nested loops are sometimes inevitable or convenient, we recommend following these guidelines to simplify your use of nested loops to help you better understand your code:</p>
<ol type="1">
<li>Use nested loops when you have a single accumulator that can be initialized just once before the nested loop (e.g., <code>sum_all</code> and <code>cartesian_product</code>).</li>
<li>If you have a nested loop where the inner loop can be replaced by a built-in aggregation function (e.g., <code>sum</code> or <code>len</code>), use the built-in function instead.</li>
<li>If you have a nested loop where the inner loop has a separate accumulator that is assigned inside the outer loop (e.g., <code>course_averages_v3</code>), move the accumulator and inner loop into a new function, and call that function from within the original outer loop.</li>
</ol>
<!-- ## From nested quantifiers to nested for loops
Recall once again our example of who loves whom:
```python
>>> LOVES = [
... [False, True, True, False],
... [False, True, True, True],
... [False, False, True, False],
... [False, False, False, True]
... ]
```
We used this data to discover that everyone is loved by someone else:^[
This is actually [not unusual](https://www.youtube.com/watch?v=Vc_BU87ZDfg).
]
```python
>>> A = range(0, 4) # We'll represent the people as indexes from 0 to 3,
>>> B = range(0, 4) # for both A and B. We use the same variable names for clarity.
``` -->
<!-- TODO Not sure which example you'd like to do here? Are we looking at alternating qualifiers? Or multiple, non-alternating qualifiers? -->
<h2 id="references">References</h2>
<ul>
<li>CSC108 videos: Nested loops (<a href="https://youtu.be/IW4J0lwc8zE">Part 1</a>, <a href="https://youtu.be/3oSEWc2avns">Part 2</a>)</li>
</ul>
</section>
<footer>
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
</footer>
</body>
</html>