{"id":"l-pda-1-5","courseId":"python-data-analysis","lang":"en","kind":"lesson","summary":"Package repeated logic into functions with parameters and return values, format numbers for reports with f-strings, and read a Python traceback to find and fix the real problem.","html":"<p>By now you have cleaned the price <code dir=\"ltr\">&quot; $129.00&quot;</code> once, computed a line’s revenue once, and printed a few numbers. Cartwheel has 3,991 order lines. You do not want to copy the cleaning code to every place it is needed and then fix a bug in seven copies. Functions let you write the logic once, give it a name, and call it wherever you need it.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Defining a function</h2>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-run=\"python\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"python\" data-index=\"0\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">def</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> line_revenue</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(quantity, unit_price, discount</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">):</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">    \"\"\"Revenue of one order line: quantity x price, minus the discount.\"\"\"</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> round</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(quantity </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> unit_price </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> -</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> discount), </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(line_revenue(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">129.0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.15</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))   </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># 219.3</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(line_revenue(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">39.0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))          </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># discount defaults to 0.0</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(line_revenue(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">quantity</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">unit_price</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">24.0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">discount</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.25</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span></code></pre><div class=\"run-output\" data-run-output=\"0\" hidden></div></div>\n<p>The pieces: <code dir=\"ltr\">def</code>, the function’s name, parameters in parentheses, a colon, and an indented body. <code dir=\"ltr\">discount=0.0</code> is a <strong>default</strong>, used when the caller leaves it out. The triple-quoted line under <code dir=\"ltr\">def</code> is a docstring, which documents what the function does; editors show it when you hover over the name.</p>\n<p><code dir=\"ltr\">return</code> sends a value back to the caller, and the call <code dir=\"ltr\">line_revenue(2, 129.0, 0.15)</code> becomes that value. You can store it, add it, pass it to another function. Calling with names, as in the third example, makes long argument lists readable and protects you from swapping two numbers by accident.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Printing instead of returning</span></p>\n<p>A function that ends with <code dir=\"ltr\">print(total)</code> shows the number and then returns <code dir=\"ltr\">None</code>. Later, <code dir=\"ltr\">line_revenue(...) * 2</code> fails with <code dir=\"ltr\">TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'</code>. Functions that compute should <code dir=\"ltr\">return</code>; let the caller decide whether to print.</p>\n</aside>\n<h2 id=\"sec-2\" tabindex=\"-1\">Cleaning functions</h2>\n<p>Here is the money cleaner from Lesson 1.2, made reusable. It handles the dollar sign, stray spaces and thousands separators:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-run=\"python\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"python\" data-index=\"1\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">def</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> parse_money</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(text):</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">    \"\"\"Turn strings like ' $1,234.50' into the float 1234.5.\"\"\"</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    cleaned </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> text.strip().replace(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"$\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).replace(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\",\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> float</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(cleaned)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> raw </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">in</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\" $129.00\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"18.00\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"$1,234.50\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]:</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">    print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(raw, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"->\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, parse_money(raw))</span></span></code></pre><div class=\"run-output\" data-run-output=\"1\" hidden></div></div>\n<p>Give functions verb names that say what they return (<code dir=\"ltr\">parse_money</code>, <code dir=\"ltr\">line_revenue</code>, <code dir=\"ltr\">clean_country</code>) and keep each one to a single job; small functions are easy to test with a couple of <code dir=\"ltr\">print</code> calls like the loop above. Now every messy total in the export can go through one tested function. If you discover a new format next month (say, a trailing <code dir=\"ltr\">USD</code>), you fix it in one place.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">f-strings: numbers people can read</h2>\n<p>A report that says <code dir=\"ltr\">146825.4412</code> makes readers squint. An <strong>f-string</strong>, a string with <code dir=\"ltr\">f</code> before the opening quote, lets you drop values into text inside <code dir=\"ltr\">{}</code> and format them with a spec after a colon:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-run=\"python\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"python\" data-index=\"2\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">revenue </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 146825.4412</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 642</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">share </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0.4493</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">f</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Q4 2025 revenue: $</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">revenue</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:,.2f}</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)       </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># thousands separator, 2 decimals</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">f</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Orders: </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">, average </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">revenue </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">/</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:.2f}</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> per order\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">f</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Share of the year: </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">share</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:.1%}</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)          </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># percent with 1 decimal</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">f</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"|</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Egypt'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:&#x3C;12}</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">|</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:>6}</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">|\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)             </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># left- and right-aligned columns</span></span></code></pre><div class=\"run-output\" data-run-output=\"2\" hidden></div></div>\n<p>The specs you will use most: <code dir=\"ltr\">,.2f</code> for money, <code dir=\"ltr\">.1%</code> for shares (it multiplies by 100 for you), <code dir=\"ltr\">,.0f</code> for large whole numbers, and <code dir=\"ltr\">&lt;</code>/<code dir=\"ltr\">&gt;</code> with a width for aligned text. Any expression works inside the braces, including arithmetic like <code dir=\"ltr\">revenue / orders</code>.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">Reading errors without panic</h2>\n<p>When Python cannot continue, it raises an <strong>exception</strong> and prints a traceback. Beginners read it from the top and get lost. Read it from the <strong>bottom</strong>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>Traceback (most recent call last):\n  File &quot;report.py&quot;, line 9, in &lt;module&gt;\n    total = parse_money(row[&quot;Total&quot;])\n                        ~~~^^^^^^^^^\nKeyError: 'Total'</code></pre></div>\n<p>The last line is the error type and message: a dictionary was asked for the key <code dir=\"ltr\">'Total'</code> and does not have it. One line up is the code that failed, with markers under the exact expression, and above that, the file and line number. The fix here is the column’s real name, <code dir=\"ltr\">'total'</code>.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 200\" role=\"img\" aria-labelledby=\"l-pda-1-5-t1\"><defs><marker id=\"arrow-l-pda-1-5-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-pda-1-5-t1\">A traceback in three layers. Bottom: the error type and message, read first. Middle: the failing line of code with markers. Top: the file and line number, used to find the place in your code.</title>\n  <rect class=\"d-box\" x=\"20\" y=\"20\" width=\"430\" height=\"44\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"36\" y=\"47\">File \"report.py\", line 9</text>\n  <rect class=\"d-box\" x=\"20\" y=\"76\" width=\"430\" height=\"44\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"36\" y=\"103\">total = parse_money(row[\"Total\"])</text>\n  <rect class=\"d-box-warn\" x=\"20\" y=\"132\" width=\"430\" height=\"44\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"36\" y=\"159\">KeyError: 'Total'</text>\n  <text class=\"d-label-strong\" x=\"480\" y=\"159\">1. what went wrong</text>\n  <text class=\"d-label\" x=\"480\" y=\"103\">2. which expression</text>\n  <text class=\"d-label-muted\" x=\"480\" y=\"47\">3. where to look</text>\n  <path class=\"d-arrow\" d=\"M680 150 L680 58\" marker-end=\"url(#arrow-l-pda-1-5-1)\"/>\n</svg><figcaption>Read a traceback from the bottom up</figcaption></figure>\n<p>The errors you will meet most, and what they almost always mean:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>Error</th>\n<th>Usual cause</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code dir=\"ltr\">NameError</code></td>\n<td>A typo in a variable name, or using it before assigning it</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">KeyError</code></td>\n<td>A dictionary key or DataFrame column that does not exist (check spelling and case)</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">TypeError</code></td>\n<td>Mixing types, such as text plus a number, or calling a function with the wrong arguments</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">ValueError</code></td>\n<td>The type is right but the content is not, such as <code dir=\"ltr\">float(&quot;$65.00&quot;)</code></td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">IndexError</code></td>\n<td>A list position past the end</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">AttributeError</code></td>\n<td>Calling a method the value does not have, often on <code dir=\"ltr\">None</code></td>\n</tr>\n</tbody>\n</table></div><h3 id=\"sec-5\" tabindex=\"-1\">Handling an error you expect</h3>\n<p>Some failures are part of the data, not bugs. A messy total such as <code dir=\"ltr\">&quot;N/A&quot;</code> will never become a number. <code dir=\"ltr\">try</code> / <code dir=\"ltr\">except</code> lets you decide what happens instead:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-run=\"python\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"python\" data-index=\"4\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">def</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> parse_money_or_none</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(text):</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    try</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        return</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> float</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(text.strip().replace(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"$\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).replace(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\",\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    except</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> ValueError</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        return</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> None</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(parse_money_or_none(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"$65.00\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">), parse_money_or_none(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"N/A\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span></code></pre><div class=\"run-output\" data-run-output=\"4\" hidden></div></div>\n<p>Catch the specific exception you expect (<code dir=\"ltr\">ValueError</code> here) and nothing else. A bare <code dir=\"ltr\">except:</code> would also swallow a typo in your own code, and you would get <code dir=\"ltr\">None</code> everywhere with no clue why.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Search the last line</span></p>\n<p>When an error message is new to you, copy its last line, minus your own variable names, into a search engine. Somebody has hit it before, and the first answer usually explains it in a paragraph.</p>\n</aside>\n<p>That completes your Python toolkit. Section 2 puts it to work at scale: pandas, where a single line operates on every row of a table.</p>\n","toc":[{"id":"sec-1","text":"Defining a function","level":2},{"id":"sec-2","text":"Cleaning functions","level":2},{"id":"sec-3","text":"f-strings: numbers people can read","level":2},{"id":"sec-4","text":"Reading errors without panic","level":2},{"id":"sec-5","text":"Handling an error you expect","level":3}],"takeaways":["A function names a piece of logic; parameters are its inputs and <code dir=\"ltr\">return</code> hands back its output.","A function that prints instead of returning gives back <code dir=\"ltr\">None</code>, so its result cannot be used in further calculations.","f-strings format values inline: <code dir=\"ltr\">f&quot;{revenue:,.2f}&quot;</code> gives <code dir=\"ltr\">1,234.50</code> and <code dir=\"ltr\">f&quot;{share:.1%}&quot;</code> gives <code dir=\"ltr\">12.5%</code>.","Read a traceback from the bottom - the last line names the error type and message, the lines above show where it happened.","<code dir=\"ltr\">try</code> / <code dir=\"ltr\">except</code> handles an error you expect, such as text that cannot become a number; never use it to hide errors you do not understand."],"further":[{"title":"Defining Functions","url":"https://docs.python.org/3/tutorial/controlflow.html#defining-functions"},{"title":"Formatted String Literals (f-strings)","url":"https://docs.python.org/3/tutorial/inputoutput.html#formatted-string-literals"},{"title":"Errors and Exceptions","url":"https://docs.python.org/3/tutorial/errors.html"},{"title":"Format Specification Mini-Language","url":"https://docs.python.org/3/library/string.html#format-specification-mini-language"}],"quiz":[{"q":"<p>What is <code dir=\"ltr\">result</code> after this runs?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">def</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> add_tax</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(amount):</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">    print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(amount </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1.2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">result </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> add_tax(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">100</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">120.0</code>","why":"The function displays 120.0, but displaying is not returning. Without <code dir=\"ltr\">return</code>, the caller gets nothing back."},{"text":"<code dir=\"ltr\">None</code>","why":"Correct. A function without a <code dir=\"ltr\">return</code> statement returns <code dir=\"ltr\">None</code>, whatever it printed along the way."},{"text":"The string <code dir=\"ltr\">&quot;120.0&quot;</code>","why":"<code dir=\"ltr\">print</code> writes text to the output; it does not hand that text back to the caller."}],"answer":1},{"q":"<p>Which f-string prints <code dir=\"ltr\">Revenue: $146,825.44</code> when <code dir=\"ltr\">revenue = 146825.4412</code>?</p>","options":[{"text":"<code dir=\"ltr\">f&quot;Revenue: ${revenue:.2f}&quot;</code>","why":"This gives <code dir=\"ltr\">$146825.44</code>, with no thousands separator. The comma in the format spec adds it."},{"text":"<code dir=\"ltr\">f&quot;Revenue: ${revenue:,}&quot;</code>","why":"The comma adds separators but there is no precision, so you get all the decimals of the float."},{"text":"<code dir=\"ltr\">f&quot;Revenue: ${revenue:,.2f}&quot;</code>","why":"Correct. <code dir=\"ltr\">,</code> adds thousands separators and <code dir=\"ltr\">.2f</code> fixes two decimal places, rounding the value for display."},{"text":"<code dir=\"ltr\">f&quot;Revenue: {round(revenue)}&quot;</code>","why":"This drops the cents and the dollar sign entirely."}],"answer":2},{"q":"<p>The last line of a traceback reads:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>KeyError: 'Total'</code></pre></div>\n<p>What is the most likely cause?</p>\n","options":[{"text":"The code asks a dictionary (or DataFrame) for a key <code dir=\"ltr\">'Total'</code> that does not exist; the real key is probably <code dir=\"ltr\">'total'</code>.","why":"Correct. <code dir=\"ltr\">KeyError</code> always names the missing key. Keys are case-sensitive, so compare the spelling with the actual keys."},{"text":"The value of <code dir=\"ltr\">total</code> cannot be converted to a number.","why":"A failed conversion raises <code dir=\"ltr\">ValueError</code>, not <code dir=\"ltr\">KeyError</code>."},{"text":"The variable <code dir=\"ltr\">Total</code> was never defined.","why":"An undefined variable raises <code dir=\"ltr\">NameError: name 'Total' is not defined</code>. The quotes in the message point to a key lookup."}],"answer":0},{"q":"<p>When is wrapping code in <code dir=\"ltr\">try</code> / <code dir=\"ltr\">except</code> a good idea?</p>","options":[{"text":"Around a whole script, so it never crashes in front of your manager.","why":"That hides every bug, including the ones that make your numbers wrong. A visible crash is better than a silent wrong answer."},{"text":"Around one operation that you expect to fail for some inputs, with a specific exception type and a clear fallback.","why":"Correct. For example, catching <code dir=\"ltr\">ValueError</code> when converting user-typed text, and returning <code dir=\"ltr\">None</code> for values that are not numbers."},{"text":"Whenever you see a <code dir=\"ltr\">SyntaxError</code>.","why":"Syntax errors happen before the code runs, so <code dir=\"ltr\">try</code> cannot catch them; you fix the code instead."}],"answer":1}],"exercise":{"kind":"python","files":[],"starter":"def parse_money(text):\n    \"\"\"' $1,234.50' -> 1234.5\"\"\"\n    pass\n\n\ndef line_revenue(quantity, unit_price, discount=0.0):\n    \"\"\"quantity x unit_price x (1 - discount), rounded to 2 decimals\"\"\"\n    pass\n\n\ndef format_money(amount):\n    \"\"\"1234.5 -> '$1,234.50'\"\"\"\n    pass\n\n\nprint(parse_money(\" $1,234.50\"), line_revenue(2, 129.0, 0.15), format_money(1234.5))\n","solution":"def parse_money(text):\n    \"\"\"' $1,234.50' -> 1234.5\"\"\"\n    return float(text.strip().replace(\"$\", \"\").replace(\",\", \"\"))\n\n\ndef line_revenue(quantity, unit_price, discount=0.0):\n    \"\"\"quantity x unit_price x (1 - discount), rounded to 2 decimals\"\"\"\n    return round(quantity * unit_price * (1 - discount), 2)\n\n\ndef format_money(amount):\n    \"\"\"1234.5 -> '$1,234.50'\"\"\"\n    return f\"${amount:,.2f}\"\n\n\nprint(parse_money(\" $1,234.50\"), line_revenue(2, 129.0, 0.15), format_money(1234.5))\n","checks":[{"id":"parse-simple","test":"parse_money('18.00') == 18.0 and parse_money(' $129.00') == 129.0"},{"id":"parse-thousands","test":"parse_money('$1,234.50') == 1234.5"},{"id":"revenue","test":"line_revenue(2, 129.0, 0.15) == 219.3 and line_revenue(3, 24.0, 0.25) == 54.0"},{"id":"revenue-default","test":"line_revenue(1, 39.0) == 39.0"},{"id":"format","test":"format_money(1234.5) == '$1,234.50' and format_money(18) == '$18.00'"}],"title":"Three helper functions for the report","prompt":"<p>Complete three functions. <code dir=\"ltr\">parse_money(text)</code> returns a float from strings like <code dir=\"ltr\">&quot; $1,234.50&quot;</code>, <code dir=\"ltr\">&quot;$129.00&quot;</code> or <code dir=\"ltr\">&quot;18.00&quot;</code>. <code dir=\"ltr\">line_revenue(quantity, unit_price, discount=0.0)</code> returns quantity × unit price × (1 − discount), rounded to 2 decimals; the discount defaults to 0. <code dir=\"ltr\">format_money(amount)</code> returns a string like <code dir=\"ltr\">&quot;$1,234.50&quot;</code>: a dollar sign, thousands separators and exactly two decimals. Each function must <code dir=\"ltr\">return</code> its result, not print it.</p>\n","hints":["Replace <code dir=\"ltr\">pass</code> with a <code dir=\"ltr\">return</code> statement in each function.","For <code dir=\"ltr\">parse_money</code>, chain <code dir=\"ltr\">.strip()</code>, <code dir=\"ltr\">.replace(&quot;$&quot;, &quot;&quot;)</code> and <code dir=\"ltr\">.replace(&quot;,&quot;, &quot;&quot;)</code>, then wrap it all in <code dir=\"ltr\">float()</code>.","For <code dir=\"ltr\">format_money</code>, an f-string with the spec <code dir=\"ltr\">,.2f</code> does all the formatting: <code dir=\"ltr\">f&quot;${amount:,.2f}&quot;</code>."],"explanation":"<p>Each function does one job and returns its result, so they compose: <code dir=\"ltr\">format_money(line_revenue(2, parse_money(&quot;$129.00&quot;), 0.15))</code> gives <code dir=\"ltr\">&quot;$219.30&quot;</code>. The default <code dir=\"ltr\">discount=0.0</code> means callers can leave it out for full-price lines. In <code dir=\"ltr\">format_money</code>, the first <code dir=\"ltr\">$</code> is literal text and the braces hold the value with the spec <code dir=\"ltr\">,.2f</code>: comma separators, fixed two decimals. You will reuse exactly this logic on whole pandas columns in Section 3.</p>\n","checkLabels":{"parse-simple":"<code dir=\"ltr\">parse_money</code> handles <code dir=\"ltr\">&quot;18.00&quot;</code> and <code dir=\"ltr\">&quot; $129.00&quot;</code>","parse-thousands":"<code dir=\"ltr\">parse_money</code> handles thousands separators: <code dir=\"ltr\">&quot;$1,234.50&quot;</code> becomes 1234.5","revenue":"<code dir=\"ltr\">line_revenue</code> applies the discount and rounds to 2 decimals","revenue-default":"<code dir=\"ltr\">line_revenue</code> works without a discount argument","format":"<code dir=\"ltr\">format_money(1234.5)</code> returns <code dir=\"ltr\">&quot;$1,234.50&quot;</code>"}},"words":709,"readMinutes":3}