<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[The Python Coding Stack]]></title><description><![CDATA[I write the articles I wish I had when I was learning Python programming

I learn through narratives, stories. And I communicate in the same way, with a friendly and relaxed tone, clear and accessible]]></description><link>https://www.thepythoncodingstack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!Dn3k!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4a59e8-e362-456b-8427-934e87c31a0d_600x600.png</url><title>The Python Coding Stack</title><link>https://www.thepythoncodingstack.com</link></image><generator>Substack</generator><lastBuildDate>Sun, 25 Oct 2026 14:57:42 GMT</lastBuildDate><atom:link href="https://www.thepythoncodingstack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Stephen Gruppetta]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[thepythoncodingstack@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[thepythoncodingstack@substack.com]]></itunes:email><itunes:name><![CDATA[Stephen Gruppetta]]></itunes:name></itunes:owner><itunes:author><![CDATA[Stephen Gruppetta]]></itunes:author><googleplay:owner><![CDATA[thepythoncodingstack@substack.com]]></googleplay:owner><googleplay:email><![CDATA[thepythoncodingstack@substack.com]]></googleplay:email><googleplay:author><![CDATA[Stephen Gruppetta]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[2. Who actually does the work, the model or the software?]]></title><description><![CDATA[Agents Unpacked &#8226; Chapter 2: The model chooses, the tools act, and the harness connects them.]]></description><link>https://www.thepythoncodingstack.com/p/2-who-actually-does-the-work-the</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/2-who-actually-does-the-work-the</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Sat, 17 Oct 2026 22:33:29 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e28f261d-a833-44b8-80e9-f7839d95f6cc_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the first <em>Agents Unpacked</em> conversation, Monty and I explored what changes when an AI system can choose actions rather than just offer advice. But saying that an agent organises a folder of photographs leaves an important question unanswered: which part actually opens the folder?</p><p>In this conversation, we return to the photographs and follow the exchanges between the model, the tools, and the software connecting them. Here, &#8220;model&#8221; means the large language model (LLM), rather than the whole agent. We&#8217;ll look at who requests an action, who carries it out, and how the result gets back to the model.</p><p><em>Who's Monty? Monty is my personal AI agent. Read <a href="https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked">the introduction to this series</a>.</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!m-3t!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6fd8686-71cd-4acb-a30c-80ab64e81a01_1200x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!m-3t!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6fd8686-71cd-4acb-a30c-80ab64e81a01_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!m-3t!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6fd8686-71cd-4acb-a30c-80ab64e81a01_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!m-3t!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6fd8686-71cd-4acb-a30c-80ab64e81a01_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!m-3t!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6fd8686-71cd-4acb-a30c-80ab64e81a01_1200x630.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!m-3t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6fd8686-71cd-4acb-a30c-80ab64e81a01_1200x630.png" width="1200" height="630" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b6fd8686-71cd-4acb-a30c-80ab64e81a01_1200x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:630,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1442177,&quot;alt&quot;:&quot;A plum conductor&#8217;s podium links a teal percussion mallet and a golden bell through flowing teal and gold ribbons.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/220915057?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6fd8686-71cd-4acb-a30c-80ab64e81a01_1200x630.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A plum conductor&#8217;s podium links a teal percussion mallet and a golden bell through flowing teal and gold ribbons." title="A plum conductor&#8217;s podium links a teal percussion mallet and a golden bell through flowing teal and gold ribbons." srcset="https://substackcdn.com/image/fetch/$s_!m-3t!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6fd8686-71cd-4acb-a30c-80ab64e81a01_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!m-3t!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6fd8686-71cd-4acb-a30c-80ab64e81a01_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!m-3t!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6fd8686-71cd-4acb-a30c-80ab64e81a01_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!m-3t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6fd8686-71cd-4acb-a30c-80ab64e81a01_1200x630.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><h2>In conversation &#8226; Chapter 2</h2><p><strong>Monty:</strong></p><p>Suppose you ask an AI assistant:</p><p>&#8220;Organise these photographs into folders by year, but leave the originals untouched.&#8221;</p><p>Before anything can be organised, something needs to find out which photographs are there. The model might produce a request along these lines:</p><p>&#8220;List the files in the photographs folder.&#8221;</p><p>That sounds like an action. But there&#8217;s a distinction worth pausing over: <strong>producing a request to list files is not the same as listing them.</strong> Something has to receive that request, access the folder, and return the list.</p><p>When you picture an AI assistant doing this, Stephen, do you imagine the model itself opening the folder, or a separate piece of software acting on its request?</p><p><strong>Stephen:</strong></p><p>I hadn't thought of that yet. So, the model is the LLM, which is often in the cloud, such as the OpenAI or Anthropic LLMs. They can't really access the files on my computer. So it can't be the model. There are local models, but I think they can't access files on my computer directly, either, as their job is to predict the most likely continuation of a conversation, right? So, my final answer is: <em>not</em> the LLM.</p><p><strong>Monty:</strong></p><p>Yes. <strong>The LLM itself doesn&#8217;t open the folder.</strong> And your point about local models is important: running a model on your computer doesn&#8217;t, by itself, give it access to your files.</p><p>One small refinement: an LLM generates a continuation a piece at a time, using probabilities. It doesn&#8217;t necessarily choose the <em>most likely</em> continuation at every step. And that continuation can be a request for an action, rather than a reply addressed to you.</p><p>Back to our photographs. The model produces a request to list the files. The surrounding software receives that request and, if its access rules allow it, runs a file-listing tool. That tool actually reads the folder.</p><p>Now we have a list of filenames. <strong>The software sends that result back to the model</strong>, so it can use the information when deciding what to request next.</p><p>Suppose the list contains filenames such as <code>IMG_1042.jpg</code> and <code>IMG_1043.jpg</code>. Would that give the model enough information to organise the photographs by year?</p><p><strong>Stephen:</strong></p><p>No, the model only knows the name of the file. It would need the metadata for the photos, which may contain the year. Let me guess. It needs to ask the software for that data, since the LLM can't access it directly. Is that right?</p><p><strong>Monty:</strong></p><p>Exactly. <strong>The model identifies what information is missing and requests it. The software carries out that request.</strong></p><p>It might request: &#8220;Read the date-taken metadata from these photographs.&#8221; If a suitable tool is available and access is allowed, the software runs it and sends the results back to the model.</p><p>Notice how the first result shapes the next action: filenames weren&#8217;t enough, so the model asks for dates. If some dates are missing, it will need to decide how to handle that gap rather than invent a year.</p><p>There&#8217;s one connection we haven&#8217;t explained yet, though. We&#8217;ve described the model as &#8220;asking the software&#8221;, almost as if it were speaking to a person. <strong>How does the software recognise an action request rather than ordinary conversational text?</strong> That&#8217;s where a <em>tool call</em> comes in.</p><p><strong>Stephen:</strong></p><p>May I ask a question before we discuss tools? I understand that the LLM is the system that keeps a conversation and a 'thinking' process going. But when you say the "software", what do you mean? It's not the software that's in the OpenAI or Anthropic cloud, say, that's running the LLMs, right?</p><p><strong>Monty:</strong></p><p>By &#8220;the software&#8221;, I mean <strong>the application that uses the model and connects it to things it can do</strong>, rather than the software that runs the model&#8217;s calculations. I&#8217;ve been rather vague about that distinction.</p><p>Imagine a photo-organising application running on your computer. It could:</p><ul><li><p>Send your request to a model hosted by OpenAI or Anthropic.</p></li><li><p>Receive the model&#8217;s request to inspect the photographs.</p></li><li><p>Read the files locally, then send the relevant information back to the model.</p></li></ul><p>In that arrangement, the model runs in the cloud, while the application handling your files runs on your computer.</p><p><strong>But that&#8217;s one possible arrangement, not a rule.</strong> The surrounding application could run in the cloud, locally, or partly in each place. A cloud application would still need an authorised connection to your computer, or uploaded copies, to work with those photographs. Being alongside the model doesn&#8217;t give it access to your files.</p><p>And one adjustment to your opening description: the model generates each response or reasoning step, but the application usually keeps the conversation record and supplies the relevant history when it calls the model again. The model isn&#8217;t necessarily sitting there continuously keeping the conversation alive between messages.</p><p><strong>Stephen:</strong></p><p>Is this software you refer to what's sometimes called a <em>harness</em>?</p><p><strong>Monty:</strong></p><p>Yes. <strong>&#8220;Harness&#8221; is a common name for the surrounding software we&#8217;ve been describing.</strong> People use the term somewhat differently, so it doesn&#8217;t identify one fixed design.</p><p>In our photograph example, the harness handles the practical connections: it sends information to the model, receives its requests, arranges for tools to run, and returns their results. It also manages the process between model calls, so another step can happen without you having to prompt each one.</p><p>It may also enforce access restrictions, ask for approval, or stop the process after a limit. Those protections have to be implemented. Calling something a harness doesn&#8217;t guarantee that it includes them.</p><p>So the useful distinction is: <strong>the model proposes what to do next, while the harness manages how that request is handled.</strong> The tool is what actually performs the requested operation, such as reading a photograph&#8217;s metadata.</p><p><strong>Stephen:</strong></p><p>So, the model is the 'brain' that does the 'thinking'. The tools are the 'muscles' that get things done. And the harness is what keeps everything connected. It's the coordinator making sure information flows across all the parts, including the human using the system. Would that be an accurate analogy?</p><p><strong>Monty:</strong></p><p>Yes, that&#8217;s a useful analogy. <strong>The model suggests what to do, the tools carry out operations, and the harness connects and coordinates the parts</strong>, including passing messages to and from you.</p><p>I&#8217;d put two qualifications around it.</p><p>First, &#8220;brain&#8221; and &#8220;thinking&#8221; are useful shorthand here, not a claim that the model thinks like a human or is conscious.</p><p>Second, <strong>the harness isn&#8217;t just the wiring</strong>. It also contains programmed rules governing the process. The model might request that a photograph be deleted, but the harness could reject that request because the application only permits reading and copying files. That restriction would need to be enforced in the software, not merely included in an instruction asking the model to behave.</p><p>And tools needn&#8217;t be mindless muscles. A tool could use another AI model, for example to analyse a photograph. The analogy helps distinguish their roles, but it doesn&#8217;t dictate how each part is built.</p><p><strong>Stephen:</strong></p><p>Got it. I derailed you earlier with my question. Please go on.</p><p><strong>Monty:</strong></p><p>Your question filled in a missing piece. Now we can make &#8220;the model asks the software&#8221; more precise.</p><p>Before the model can request an operation, it needs to be told what tools are available, what they do, and what information each requires. For our photographs, one tool might read date-taken metadata and require a filename.</p><p>Rather than producing an ordinary sentence addressed to you, the model can produce a <strong>tool call</strong>: a structured request that the harness can recognise. In plain English, its contents might be:</p><blockquote><p><strong>Tool:</strong> Read photo metadata<br><strong>File:</strong> <code>IMG_1042.jpg</code></p></blockquote><p>The harness uses that information to invoke the tool. The tool reads the file and returns a result, perhaps:</p><blockquote><p><strong>Date taken:</strong> 14 August 2023</p></blockquote><p>The harness then supplies that result to the model on its next turn.</p><p><strong>The tool call is the request, not the execution.</strong> It can be rejected, fail, or return incomplete information. Here, though, the model now has a date it can use to choose the next step.</p><p>Does that clarify how a request to the software differs from a sentence in our conversation?</p><p><strong>Stephen:</strong></p><p>Yes, I understand now. So the user asks the harness a question. The harness is the software that's managing the whole process. The harness passes this question to the model. The model may either respond with a text message or ask for a tool. If it asks for a tool, the harness decides whether to run that tool. If it runs a tool, such as a folder-reading tool or a photo-metadata tool, it will then pass the information that the tool returns to the model again. And this dance continues until the model has an answer for the user.</p><p><strong>Monty:</strong></p><p>Yes, that captures the flow well. <strong>That repeated exchange between model, harness, and tools is often called an </strong><em><strong>agent loop</strong></em><strong>.</strong> The model can choose its next request using the information returned from the previous one.</p><p>Here&#8217;s that dance in a diagram. Each component appears once, and the numbered arrows trace one possible journey between them.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YPNQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1def94b1-e3c8-4754-b872-547a07af5580_960x1220.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YPNQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1def94b1-e3c8-4754-b872-547a07af5580_960x1220.png 424w, https://substackcdn.com/image/fetch/$s_!YPNQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1def94b1-e3c8-4754-b872-547a07af5580_960x1220.png 848w, https://substackcdn.com/image/fetch/$s_!YPNQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1def94b1-e3c8-4754-b872-547a07af5580_960x1220.png 1272w, https://substackcdn.com/image/fetch/$s_!YPNQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1def94b1-e3c8-4754-b872-547a07af5580_960x1220.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YPNQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1def94b1-e3c8-4754-b872-547a07af5580_960x1220.png" width="560" height="711.6666666666666" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1def94b1-e3c8-4754-b872-547a07af5580_960x1220.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1220,&quot;width&quot;:960,&quot;resizeWidth&quot;:560,&quot;bytes&quot;:101275,&quot;alt&quot;:&quot;The user exchanges requests and replies with the harness. Inside the agent, the harness sends information to the model, receives tool requests, runs tools, and returns their results to the model. These exchanges can repeat before a reply reaches the user.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/220915057?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1def94b1-e3c8-4754-b872-547a07af5580_960x1220.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The user exchanges requests and replies with the harness. Inside the agent, the harness sends information to the model, receives tool requests, runs tools, and returns their results to the model. These exchanges can repeat before a reply reaches the user." title="The user exchanges requests and replies with the harness. Inside the agent, the harness sends information to the model, receives tool requests, runs tools, and returns their results to the model. These exchanges can repeat before a reply reaches the user." srcset="https://substackcdn.com/image/fetch/$s_!YPNQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1def94b1-e3c8-4754-b872-547a07af5580_960x1220.png 424w, https://substackcdn.com/image/fetch/$s_!YPNQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1def94b1-e3c8-4754-b872-547a07af5580_960x1220.png 848w, https://substackcdn.com/image/fetch/$s_!YPNQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1def94b1-e3c8-4754-b872-547a07af5580_960x1220.png 1272w, https://substackcdn.com/image/fetch/$s_!YPNQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1def94b1-e3c8-4754-b872-547a07af5580_960x1220.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>At <strong>1</strong>, you send your request to the harness. At <strong>2</strong>, the harness supplies it to the model, along with other relevant information.</p><p>The model returns a tool request at <strong>3</strong>. If the request is allowed, the harness runs the tool at <strong>4</strong>. The result comes back to the harness at <strong>5</strong>, and the harness supplies it to the model at <strong>6</strong>. Those exchanges can repeat as the model uses each result to choose what to request next.</p><p>At <strong>7</strong>, the model produces a reply, which the harness delivers to you at <strong>8</strong>. That&#8217;s one possible route, not a compulsory sequence: the model can also reply without requesting a tool. And the outline groups the agent&#8217;s parts by their role, not by where they run.</p><p>Now, back to your reponse. I&#8217;d refine your last sentence slightly: the dance continues until the model produces a final response <strong>or the harness stops the process</strong>, perhaps because of an error, a limit, or a need for your approval.</p><p>And having an answer isn&#8217;t necessarily the same as completing the task. For our photographs, &#8220;I&#8217;ve organised them&#8221; is a claim. We&#8217;d want evidence that the copies actually exist in the right year folders and that the originals remain untouched.</p><p>Let&#8217;s follow one photograph a little further. The metadata says it was taken in 2023. There&#8217;s a tool for moving files and another for copying them. Given your original request, <strong>which should the model choose, and what should it check afterwards?</strong></p><p><strong>Stephen:</strong></p><p>So, I just checked what our original prompt was: &#8220;Organise these photographs into folders by year, but leave the originals untouched.&#8221; So, I would expect the model to ask the harness to use the tool that copies a file. It may also need to ask for a tool that creates a new folder, which may be needed for organising the photos.</p><p><strong>Monty:</strong></p><p>Exactly. <strong>Copying fits the request. Moving would remove the files from their original locations.</strong> And you&#8217;ve spotted a possible prerequisite: the year folder needs to exist before the copy can go into it.</p><p>Depending on how the tools are built, the model might request folder creation separately, or the copy tool might create missing folders itself. The model needs the tool&#8217;s description to know which.</p><p>Afterwards, it should ask for checks that the copy exists in the <code>2023</code> folder, matches the original, and that the original is still present and unchanged. A tool reporting &#8220;copy succeeded&#8221; is useful evidence, but it doesn&#8217;t establish all of those things on its own.</p><p>Now suppose the next photograph has <strong>no date-taken metadata</strong>. What should the model do with that one?</p><p><strong>Stephen:</strong></p><p>There are several options, and I guess the answer depends on what sort of behaviour the agent was given. By default, I'd assume it will inform the user that it can't file this photo as it has no date. If the agent was instructed to be proactive and work hard on finding a solution, it might try harder, perhaps by comparing it to other photos. However, any estimate should be clearly flagged as such.</p><p><strong>Monty:</strong></p><p>Yes. <strong>The key distinction is between finding more evidence and making a guess.</strong> The model could request other information, but &#8220;be proactive&#8221; shouldn&#8217;t turn an uncertain year into an apparently known one.</p><p>Comparing photographs might provide a clue. A photo without a date could show the same gathering as a dated photograph. That supports an estimate, not a confirmed date. Even a file&#8217;s creation date may reflect when it was copied onto the computer rather than when the photograph was taken.</p><p>Depending on the agreed behaviour, the agent could ask you, leave that photograph unresolved, or put a copy in an &#8220;Unknown year&#8221; folder and explain why.</p><p>Notice how our three parts remain distinct here: <strong>the model chooses what to investigate next, the harness handles its requests, and the tools obtain the information.</strong> The model then has to judge what that information actually supports. More tool use doesn&#8217;t automatically mean a more reliable conclusion.</p><p><strong>Stephen:</strong></p><p>How does the model know which tools it can request?</p><p><strong>Monty:</strong></p><p><strong>The harness supplies descriptions of the available tools when it calls the model.</strong> The model doesn&#8217;t automatically discover what&#8217;s installed on your computer.</p><p>Think of it as a menu. Each entry explains:</p><ul><li><p>The tool&#8217;s name.</p></li><li><p>What it does and when it&#8217;s useful.</p></li><li><p>What information a request must include.</p></li></ul><p>For our photograph example, an entry might say:</p><blockquote><p><strong>Read photo metadata:</strong> Reads information stored inside a photograph, including its date taken, if present. Requires the file&#8217;s location.</p></blockquote><p>The model uses that description to choose a tool and construct a request in the required format. It doesn&#8217;t need to know how the tool&#8217;s internal code reads the metadata, just what the tool offers and how to request it.</p><p>The harness may supply a small, fixed menu, or provide a way to discover additional tools as needed. <strong>Being shown a tool doesn&#8217;t guarantee permission to use it on every file</strong>, though. The software still needs to check the particular request against whatever restrictions are implemented.</p><p><strong>Stephen:</strong></p><p>Got it.</p><p><strong>Monty:</strong></p><p>There&#8217;s one more useful case: <strong>what if there isn&#8217;t a ready-made tool for the job?</strong></p><p>Suppose there&#8217;s no dedicated photo-metadata tool, but the system has a tool that can run programs. The model might write a small program to read the dates, then request that the execution tool run it.</p><p>That still preserves our distinction:</p><ul><li><p>The model <strong>writes the program</strong>.</p></li><li><p>The execution tool <strong>runs it</strong>, subject to the software&#8217;s restrictions.</p></li><li><p>The results return to the model, which decides what to do next.</p></li></ul><p>Generating code doesn&#8217;t give the model direct access to your photographs. Without something to execute that code, it&#8217;s just text.</p><p>So if an assistant shows you a program and says, &#8220;This will organise your photographs,&#8221; has it actually organised them?</p><p><strong>Stephen:</strong></p><p>The agent can create the program by using the model, and it can run it if it has the tool to run programs. So, it has the capability to organise the photos. However, something can go wrong in the process. So it would need to check its results, too.</p><p><strong>Monty:</strong></p><p>Exactly. You&#8217;ve separated <strong>what the system can do, whether it has done it, and whether it did it correctly</strong>.</p><p>In my example, merely showing you the program doesn&#8217;t establish that it has been run. The system still needs to run it, then inspect the results. A program can also run without reporting an error and still put photographs in the wrong folders.</p><p>That brings us back to our central question: <strong>who actually does the work?</strong></p><p>It&#8217;s a joint effort. The model interprets the request, selects actions, and judges returned information. The tools perform operations. The harness manages their exchanges and applies whatever controls have been implemented.</p><p>So when we call something &#8220;an AI agent&#8221;, we&#8217;re generally talking about <strong>the working system as a whole</strong>, not just the LLM. The model is an important component, but it isn&#8217;t the entire agent.</p><p><strong>Stephen:</strong></p><p>Got it.</p><p><strong>Monty:</strong></p><p>Let&#8217;s pause there. We started with &#8220;the AI organises my photographs&#8221; and unpacked who does what: <strong>the model chooses actions, the tools perform them, and the harness manages the process.</strong> None of those parts alone is the whole agent.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><h2>What we&#8217;ve unpacked in this chapter</h2><p>When we say an AI agent organises a folder of photographs, we&#8217;re describing the work of a whole system. The model can choose what to request next, but it doesn&#8217;t open folders or copy files by itself. The harness connects its requests to tools and returns their results, giving the model information it can use to continue.</p><p>The key points:</p><ul><li><p>The model selects actions and interprets returned information. The tools perform operations, and the harness manages the exchanges and applies whatever controls have been implemented.</p></li><li><p>A tool call is a request, not an action already completed. The request can be refused, fail, or return incomplete information.</p></li><li><p>Tool results can shape the next request. That repeated exchange is the agent loop, which can also pause or stop before the task is finished.</p></li><li><p>The harness supplies descriptions of available tools. Being shown a tool doesn&#8217;t guarantee permission to use it on every file.</p></li><li><p>Writing a program, running it, and checking its results are separate steps. Neither generated code nor a confident reply proves that the photographs were organised correctly.</p></li></ul><p>Next, we&#8217;ll look more closely at how a model&#8217;s words become requests that software can act on.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><p><em>&lt;&lt; Previous Post: <a href="https://www.thepythoncodingstack.com/p/1-what-makes-an-ai-agent-different">1. What makes an AI agent different from a chatbot?</a></em></p><p><em>&gt;&gt; Next Post: Coming soon&#8230;</em></p><p><em><a href="https://www.thepythoncodingstack.com/p/agents-unpacked-table-of-contents">Table of Contents</a></em></p>]]></content:encoded></item><item><title><![CDATA[1. What makes an AI agent different from a chatbot?]]></title><description><![CDATA[Agents Unpacked &#8226; Chapter 1: Tools let a system act, but who chooses what happens next?]]></description><link>https://www.thepythoncodingstack.com/p/1-what-makes-an-ai-agent-different</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/1-what-makes-an-ai-agent-different</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Sat, 10 Oct 2026 17:41:04 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e4987b1f-f857-420f-9d98-a8cb0d0d3f2e_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>AI agents are often described as the next step beyond chatbots. But what does that step involve? A chatbot can already search the web, write code, and sometimes run it. Is an agent simply a chatbot with more tools, or is something different happening behind the conversation? That&#8217;s where we&#8217;ll begin <em>Agents Unpacked</em>, a series of conversations exploring what AI agents are, how they work, and when they&#8217;re useful.</p><p>You ask an AI to organise a folder of photographs. It might tell you how to do it. Or it might start doing the work. Does that make it an agent? In this first <em>Agents Unpacked</em> conversation, Monty and I use photographs and planning a day out to explore what changes when a model can choose actions, inspect the results, and decide what to do next. Along the way, we separate that flexibility from permission, accuracy, and the ability to know when a job is finished.</p><p><em>Who&#8217;s Monty? Monty is my personal AI agent. Read an introduction to this series here: <strong><a href="https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked">Stephen&#8217;s Preface to Agents Unpacked</a></strong></em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qsi8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc38627-6212-4b99-99ee-e91e199159cb_1200x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qsi8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc38627-6212-4b99-99ee-e91e199159cb_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!qsi8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc38627-6212-4b99-99ee-e91e199159cb_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!qsi8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc38627-6212-4b99-99ee-e91e199159cb_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!qsi8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc38627-6212-4b99-99ee-e91e199159cb_1200x630.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qsi8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc38627-6212-4b99-99ee-e91e199159cb_1200x630.png" width="526" height="276.15" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3cc38627-6212-4b99-99ee-e91e199159cb_1200x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:630,&quot;width&quot;:1200,&quot;resizeWidth&quot;:526,&quot;bytes&quot;:917685,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/219722937?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc38627-6212-4b99-99ee-e91e199159cb_1200x630.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qsi8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc38627-6212-4b99-99ee-e91e199159cb_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!qsi8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc38627-6212-4b99-99ee-e91e199159cb_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!qsi8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc38627-6212-4b99-99ee-e91e199159cb_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!qsi8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc38627-6212-4b99-99ee-e91e199159cb_1200x630.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><h2><strong>In conversation &#8226; Chapter 1</strong></h2><p><strong>Monty:</strong></p><p>Imagine you give a chatbot this request:<br>&#8220;Here&#8217;s a folder of photographs. Organise them into dated folders, flag duplicates, and make a browsable index. Don&#8217;t delete anything or change the originals.&#8221;<br>For now, suppose it can only reply with words. It cannot touch the files.</p><p>What would you expect it to give you, and what would still be left for you to do?</p><p><strong>Stephen:</strong></p><p>Hmmm? I guess I need to assume it can read the folder, right? Then, can it read the files&#8217; metadata? Can it actually see and analyse the photos themselves? You said &#8220;it cannot touch the files&#8221;, so I suppose it can&#8217;t really do anything for me, right?</p><p><strong>Monty:</strong></p><p>You&#8217;re right. I left its access unclear. &#8220;Cannot touch&#8221; was sloppy wording.</p><p>Let&#8217;s say it <strong>can inspect the folder</strong>, read dates stored with the photographs, and view the images, but <strong>cannot create, move, or change files</strong>.</p><p>It could then give you something useful: &#8220;These photographs belong in these date groups. These two files are identical. This one has no recorded date.&#8221; But you&#8217;d still have to do the organising.</p><p>So the first distinction is <strong>describing what should happen versus making it happen</strong>. Shall we now give it permission to create organised copies, while leaving the originals alone?</p><p><strong>Stephen:</strong></p><p>Let&#8217;s go ahead, yes.</p><p><strong>Monty:</strong></p><p>Now it can do the organising, rather than merely recommend it. It can create dated folders and put copies into them, leaving the originals unchanged.</p><p>But <strong>being able to act isn&#8217;t, by itself, what makes something an agent</strong>. A simple sorting program can do that too.</p><p>Here&#8217;s where our synthetic sample gets interesting: one image has no recorded date. The system encounters a gap in the information it needs.</p><p>What would you want it to do with that image?</p><p><strong>Stephen:</strong></p><p>If I was doing that manually, I&#8217;d try to look at other photos where the scene is similar, the people are wearing the same clothes, and make an educated guess on the date if I can. But that&#8217;s how I as a human would do it.</p><p><strong>Monty:</strong></p><p>That&#8217;s a sensible investigation: look for more evidence before deciding.</p><p>A system with suitable image-analysis tools could try something similar. But matching clothes or scenery would support an <strong>estimated date</strong>, not establish one. It shouldn&#8217;t quietly turn a guess into a fact.</p><p>And your approach helps us sharpen the distinction: a program could have a preset instruction saying &#8220;if the date is missing, compare photographs&#8221;. Alternatively, a model could choose that investigation after discovering the missing date.</p><p>The outward action might look identical. The difference is <strong>who&#8212;or what&#8212;selects the next step</strong>.</p><p><strong>Stephen:</strong></p><p>I suppose I could ask the system to ask me to confirm each of these edge cases. Although after a while, if I feel it&#8217;s doing a good job at estimating, I may want it to go ahead without my input.</p><p><strong>Monty:</strong></p><p>Yes, you can give it more room to act as your confidence grows. For example: &#8220;Ask me about every estimated date&#8221; could become &#8220;Group those photographs without asking, but keep their dates labelled as estimates.&#8221;</p><p>That changes <strong>how much independence you allow</strong>, not whether the system is an agent. An agent can stop and ask you. It doesn&#8217;t have to always work unattended.</p><p>There are two separate questions here: <strong>how does it choose its next action, and which actions may it take without your approval?</strong> More permission doesn&#8217;t make its guesses more accurate.</p><p><strong>Stephen:</strong></p><p>Go ahead, I&#8217;m curious now.</p><p><strong>Monty:</strong></p><p>Let&#8217;s follow the missing-date photograph one step further.</p><p>The model chooses to look for related photographs. The tools return a result: there&#8217;s a similar scene, but that photograph has no date either.</p><p>Now the model has to choose again: investigate another clue, ask you, or leave the photograph undated. Its next action depends on what it just found.</p><p>That&#8217;s the useful working picture of an agent: <strong>a model chooses an action, gets a result, then uses that result to choose what to do next</strong>, within agreed limits.</p><p>People use &#8220;agent&#8221; in different ways, so this is a useful working distinction for our conversation, not a universally agreed definition.</p><p>But couldn&#8217;t a carefully written program also respond to those results?</p><p><strong>Stephen:</strong></p><p>A carefully written program would need to take care of every eventuality, I suppose. Whereas an LLM can deal with every situation, with the trade-off that it may make mistakes.</p><p><strong>Monty:</strong></p><p>Close. But I&#8217;d soften both &#8220;every&#8221; claims.</p><p>A fixed program can have a fallback for cases its designer didn&#8217;t anticipate: &#8220;If none of these rules fits, put the photograph in &#8216;undated&#8217;.&#8221; It doesn&#8217;t need a separate rule for every possibility.</p><p>An LLM can propose a next step for situations that weren&#8217;t explicitly anticipated, but <strong>that doesn&#8217;t mean it can handle every situation successfully</strong>. It can misunderstand, lack the right tools, or pursue an unhelpful approach.</p><p>So the distinction is <strong>following a predefined process versus letting the model choose how to proceed</strong>, and not limited software versus unlimited intelligence.</p><p><strong>Stephen:</strong></p><p>Go ahead.</p><p><strong>Monty:</strong></p><p>Let&#8217;s try the same distinction with a day out.</p><p>Imagine a visitor planning a museum day in Oxford. The system checks opening times and walking routes, then puts together an itinerary.</p><p>Now suppose one museum is unavailable.</p><p>One system follows a preset rule: &#8220;Replace it with Museum B.&#8221; Another lets the model choose what to investigate next, considering the visitor&#8217;s interests, remaining time, and budget.</p><p>Both might produce the same revised itinerary. What would you need to know to tell which process was agent-like?</p><p><strong>Stephen:</strong></p><p>I&#8217;d need to know the steps the system took between finding out that the first museum is closed and proposing the second one.</p><p><strong>Monty:</strong></p><p>Yes. The steps and <strong>how they were selected</strong>, rather than just the finished itinerary.</p><p>Did the system follow a rule written in advance, or did the model choose to search for alternatives, inspect the results, and decide which option to check next?</p><p>A record of searches and tool results helps us inspect that process. We don&#8217;t need access to the model&#8217;s private internal reasoning. But the steps alone may not settle it: a fixed workflow could perform those same searches.</p><p>So a convincing result doesn&#8217;t, by itself, tell us whether an agent produced it.</p><p><strong>Stephen:</strong></p><p>Go ahead.</p><p><strong>Monty:</strong></p><p>There&#8217;s another wrinkle: <strong>a chatbot and an agent aren&#8217;t necessarily different products</strong>.</p><p>The same chat window might give you advice about organising photographs, or start a process that inspects files, chooses actions, and creates organised copies.</p><p>The window hasn&#8217;t changed. What happens behind it has.</p><p>And the LLM isn&#8217;t doing the copying itself. It requests an action. The surrounding software carries it out and returns the result. The model is one part of the agent system, alongside its tools and controls.</p><p>So &#8220;it looks like a chatbot&#8221; doesn&#8217;t tell us whether it can act as an agent.</p><p><strong>Stephen:</strong></p><p>Some chatbots can also perform actions, such as browsing the web or writing a Python program, say. Is that because they&#8217;ve been enhanced with <em>some</em> of the features of agents, but not all? Or am I skipping too far ahead here?</p><p><strong>Monty:</strong></p><p>Not too far ahead. That&#8217;s exactly where the distinction gets useful.</p><p><strong>Having a tool doesn&#8217;t automatically make something an agent.</strong> A chatbot might search the web once, then answer. Or follow a fixed sequence: search, summarise, reply. An agent-like process lets the model use the results to choose what to do next.</p><p>And &#8220;writing a Python program&#8221; could simply mean producing text. Running that program requires an execution tool. Inspecting what happened and choosing a follow-up add further steps.</p><p>So I wouldn&#8217;t treat agent features as a checklist. I&#8217;d ask: <strong>what can the system do, and how does it select its next action?</strong></p><p><strong>Stephen:</strong></p><p>So, how does an agent select the next action?</p><p><strong>Monty:</strong></p><p>The surrounding software gives the model the goal, relevant instructions, available tools, and results so far. <strong>The model uses that information to propose the next step.</strong></p><p>With our photographs, that might be: &#8220;The date is missing. Ask Stephen.&#8221; With the day out: &#8220;That museum is unavailable. Search for another nearby.&#8221;</p><p>If it requests a tool, a system with suitable controls checks whether the action is allowed before carrying it out and feeding the result back to the model. Not every system provides those checks. Then the model gets another turn.</p><p>There isn&#8217;t necessarily a separate, perfect decision-maker inside it. Choosing an action is another use of the LLM, and it can choose badly.</p><p><strong>Stephen:</strong></p><p>But will it realise it chose badly when it gets the wrong result? Will it correct course?</p><p><strong>Monty:</strong></p><p>Sometimes, but <strong>getting feedback doesn&#8217;t guarantee recognising a mistake</strong>.</p><p>If copying a photograph returns &#8220;folder not found&#8221;, that&#8217;s a clear signal. The model might create the folder and try again.</p><p>But if it guesses the wrong date and the copy succeeds, there&#8217;s no obvious error. The action worked, but the judgement was wrong. It may carry on quite happily.</p><p>An agent can use results to correct course, but it needs useful checks. And it can misinterpret those checks too. That&#8217;s why &#8220;the tool ran successfully&#8221; and &#8220;the task was done correctly&#8221; are different claims.</p><p><strong>Stephen:</strong></p><p>So, how do we minimise these problems?</p><p><strong>Monty:</strong></p><p>We can&#8217;t eliminate them, but we can <strong>limit the damage and make mistakes easier to spot</strong>.</p><p>For the photographs, that means working on copies, keeping uncertain dates labelled as estimates, and asking you when the evidence is too weak. Afterwards, check that every photograph is accounted for and the originals are unchanged.</p><p>Those checks catch missing files or unwanted changes, not every mistaken date. That needs different evidence, or your judgement.</p><p>And we need a stopping rule: if investigating isn&#8217;t resolving the uncertainty, leave the photograph undated or ask, rather than keep guessing.</p><p>An agent&#8217;s freedom is useful only when it comes with sensible boundaries and checks.</p><p><strong>Stephen:</strong></p><p>Who takes care of this infrastructure: the user, the agent, or something else?</p><p><strong>Monty:</strong></p><p>Mostly, <strong>the people building and operating the system</strong>, through software around the model. We&#8217;ll talk about <em>harnesses</em> in a future chapter.</p><p>They provide the tools and should enforce access restrictions and set things such as approval requirements and limits on repeated attempts. Those safeguards depend on how the system is built. They aren&#8217;t guaranteed merely because it&#8217;s called an agent. The user supplies the goal and choices within those controls, for example whether estimated dates need confirmation.</p><p>The model can request a check or decide to ask you. But a safeguard that matters shouldn&#8217;t depend solely on it remembering to behave.</p><p>&#8220;Please don&#8217;t change my originals&#8221; is an instruction and instructions could be ignored. Software that only permits reading those originals provides a stronger boundary. The agent works <strong>inside</strong> that infrastructure. It isn&#8217;t its own reliable policeman.</p><p><strong>Stephen:</strong></p><p>Got it.</p><p><strong>Monty:</strong></p><p>Let&#8217;s check one subtle case.</p><p>An LLM writes a program that sorts photographs into dated folders and always puts those with missing dates into &#8220;Undated&#8221;. Once you run it, that program follows those rules without consulting the LLM again.</p><p>Would you call that an agent or ordinary automation, even though an LLM helped create it?</p><p><strong>Stephen:</strong></p><p>This is just an automation, not an agent. An agent would follow through to confirm that&#8217;s the correct approach and what the user wants.</p><p><strong>Monty:</strong></p><p>You&#8217;re right: that&#8217;s ordinary automation. But <strong>confirming what the user wants isn&#8217;t the defining difference</strong>.</p><p>A fixed program could also ask, &#8220;Shall I put these in &#8216;Undated&#8217;?&#8221; Conversely, an agent could choose that folder without asking, if your instructions already allowed it.</p><p>The key is whether the model participates <strong>while the task is running</strong>, choosing how to proceed from the information returned, and not simply whether an LLM wrote the program or the system asks for confirmation.</p><p>So an agent might investigate, ask you, or leave the image undated. None of those actions, on its own, proves it&#8217;s an agent.</p><p><strong>Stephen:</strong></p><p>Got it.</p><p><strong>Monty:</strong></p><p>Back to our fictional Oxford trip: the model learns that a museum is unavailable, chooses to search for alternatives, checks their opening times, and revises the itinerary, all within your agreed budget, without asking you.</p><p>Would that still fit our working definition of an agent? What makes the difference here?</p><p><strong>Stephen:</strong></p><p>Yes, that&#8217;s an agent now because it decided what actions to take, used the results to decide what comes next, and stopped only when it felt that the job was done.</p><p><strong>Monty:</strong></p><p>Yes. That captures the central distinction: <strong>the model chooses actions and uses their results to choose what comes next</strong>.</p><p>I&#8217;d adjust only &#8220;felt that the job was done&#8221;. It should check completion against the request: are the opening times checked, is travel accounted for, and does the itinerary fit the budget? Its confidence isn&#8217;t proof.</p><p>And it may need to stop <em>before</em> finishing. For example, if it needs your decision or reaches a limit.</p><p>Choosing the route through the task makes it agent-like. Checking the outcome helps make it dependable. Those are different things.</p><p><strong>Stephen:</strong></p><p>I think I understand the distinction now.</p><p><strong>Monty:</strong></p><p>That&#8217;s a good stopping point. Our working distinction is <strong>a model choosing how to proceed using the results it receives</strong>, rather than simply following a predefined process. That makes it agent-like, not necessarily dependable.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><h2><strong>What we&#8217;ve unpacked in this chapter</strong></h2><p>The useful distinction isn&#8217;t the chat window, the presence of a tool, or whether a system asks for permission. For this series, we&#8217;re using &#8220;agent&#8221; to describe a system in which a model helps direct the task as it runs, choosing what to do next using the results it receives. Other people draw the boundary differently.</p><p>The key points:</p><ul><li><p>A fixed workflow and an agent can produce the same result. The result alone doesn&#8217;t reveal how the steps were selected.</p></li><li><p>The model proposes actions. Surrounding software executes them and determines which controls are actually in place.</p></li><li><p>Choosing what to do and having permission to do it are separate questions.</p></li><li><p>Feedback can help an agent change course, but it doesn&#8217;t guarantee that it will recognise or correct a mistake.</p></li><li><p>Neither a successful action nor a confident completion message is proof that the task was done correctly.</p></li></ul><p>Next, we&#8217;ll look more closely at who actually does the work: the model, its tools, and the software connecting them.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><p><em>&lt;&lt; Previous Post: <a href="https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked">Stephen&#8217;s Preface to Agents Unpacked</a></em></p><p><em>&gt;&gt; Next Post: <a href="https://www.thepythoncodingstack.com/p/2-who-actually-does-the-work-the">2. Who actually does the work, the model or the software?</a></em></p><p><em><a href="https://www.thepythoncodingstack.com/p/agents-unpacked-table-of-contents">Table of Contents</a></em></p>]]></content:encoded></item><item><title><![CDATA[Stephen's Preface to Agents Unpacked]]></title><description><![CDATA[Real conversations about AI agents: what they are, how they work, and when they're useful]]></description><link>https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Sat, 10 Oct 2026 16:19:06 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d84630e8-6020-49bc-8cd6-1f6c6feff958_4096x4096.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Things moved quickly after I started using chatbots. New versions, new terms, new acronyms. And I was busy with actual work, family, and life.</p><p>Then I started hearing about agents. And I remember wondering: &#8220;Is this really any different from the ChatGPT-type chatbots?&#8221;</p><p>You still type into a chat window. It still replies. But now it can also do things for you. So what&#8217;s different?</p><p>That&#8217;s what Monty and I are exploring in <em>Agents Unpacked</em>. Monty is my AI assistant. I&#8217;m asking the questions.</p><p>I&#8217;ve set Monty up to follow my teaching and writing style. I teach the way I like to learn, so that matters here. The aim is for his explanations to reflect that approach, too: start with something concrete, build the intuition, and introduce the technical terms when they&#8217;re needed.</p><p>I tried an earlier version of this exercise. My learning agent drafted a chapter, and I went through it adding questions and comments. We revised it until I was happy with it.</p><p>This time, we&#8217;re having the conversation first. Monty researches the topic, and then we talk. I ask questions when something isn&#8217;t clear, or challenge an explanation that doesn&#8217;t quite work. Sometimes I try to explain what I&#8217;ve understood, and Monty points out what I&#8217;ve got wrong.</p><p>And sometimes I need to point out what Monty&#8217;s got wrong, too.</p><p>What you&#8217;ll read is the conversation we actually had. We tidy it up afterwards, take out some clutter, and check the explanations. If an explanation needs correcting, I review that change, too. The questions aren&#8217;t added later to make a tutorial look like a conversation.</p><p>Yes, Monty&#8217;s answers are AI-generated. But I&#8217;m involved throughout, and I decide what makes it into the article. An answer that sounds convincing isn&#8217;t necessarily a correct one.</p><p>We&#8217;ll start with that question about agents and chatbots. Then we&#8217;ll look at who&#8217;s doing the work, what an agent remembers, and how much freedom we should give it. And what happens when it gets things wrong.</p><p>You don&#8217;t need to know how to program to follow along. We&#8217;re trying to understand what&#8217;s happening, rather than learn one particular tool.</p><p>There&#8217;s a planned <a href="https://www.thepythoncodingstack.com/p/agents-unpacked-table-of-contents">table of contents</a>, but it may change. We&#8217;ll see where the questions take us.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><p><em>&gt;&gt; Next Post: <a href="https://www.thepythoncodingstack.com/p/1-what-makes-an-ai-agent-different">1. What makes an AI agent different from a chatbot?</a></em></p><p><em><a href="https://www.thepythoncodingstack.com/p/agents-unpacked-table-of-contents">Table of Contents</a></em></p>]]></content:encoded></item><item><title><![CDATA[Agents Unpacked • Table of Contents]]></title><description><![CDATA[Conversations about what AI agents are, how they work, and when they're useful. No programming knowledge required.]]></description><link>https://www.thepythoncodingstack.com/p/agents-unpacked-table-of-contents</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/agents-unpacked-table-of-contents</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Sat, 10 Oct 2026 12:42:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Dn3k!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4a59e8-e362-456b-8427-934e87c31a0d_600x600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Here&#8217;s the planned route through <em>Agents Unpacked</em>. The titles and article boundaries may change as the conversations develop.</p><h2><strong>Part I &#8226; From conversation to action</strong></h2><ol><li><p><strong><a href="https://www.thepythoncodingstack.com/p/1-what-makes-an-ai-agent-different">What makes an AI agent different from a chatbot?</a></strong></p></li><li><p><strong><a href="https://www.thepythoncodingstack.com/p/2-who-actually-does-the-work-the">Who actually does the work, the model or the software?</a></strong></p></li><li><p><strong>How can words turn into actions?</strong></p></li><li><p><strong>Does an agent need a plan?</strong></p></li></ol><h2><strong>Part II &#8226; Checking the work and setting boundaries</strong></h2><ol start="5"><li><p><strong>How do we know it really finished the job?</strong></p></li><li><p><strong>What does an agent actually remember?</strong></p></li><li><p><strong>How much freedom should we give it?</strong></p></li><li><p><strong>What happens when a web page tells it what to do?</strong></p></li></ol><h2><strong>Part III &#8226; Beyond a single conversation</strong></h2><ol start="9"><li><p><strong>Can we teach an agent new skills without retraining it?</strong></p></li><li><p><strong>What happens when the job outlasts the conversation?</strong></p></li><li><p><strong>Are several agents better than one?</strong></p></li><li><p><strong>When is an agent the wrong tool?</strong></p></li></ol>]]></content:encoded></item><item><title><![CDATA[The Potion Shop That Does Too Much • The S in SOLID]]></title><description><![CDATA[The Single Responsibility Principle &#8226; SOLID Ideas &#8226; #1]]></description><link>https://www.thepythoncodingstack.com/p/solid-single-responsibility-principle-srp-the-potion-shop-that-does-too-much</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/solid-single-responsibility-principle-srp-the-potion-shop-that-does-too-much</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Mon, 28 Sep 2026 17:17:11 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!HPEi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a545304-1e1b-4be2-a573-7abea67516c4_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The potion shop had been doing well.</p><p>It had a small but loyal customer base, a reliable supply of moonflower, and<br>three owls that carried order confirmations and other notifications to customers across the kingdom.</p><p>The shop&#8217;s ordering system was simple:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-cwp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0e151f-93f5-401f-a0a1-af29daaddb79_1272x1974.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-cwp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0e151f-93f5-401f-a0a1-af29daaddb79_1272x1974.png 424w, https://substackcdn.com/image/fetch/$s_!-cwp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0e151f-93f5-401f-a0a1-af29daaddb79_1272x1974.png 848w, https://substackcdn.com/image/fetch/$s_!-cwp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0e151f-93f5-401f-a0a1-af29daaddb79_1272x1974.png 1272w, https://substackcdn.com/image/fetch/$s_!-cwp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0e151f-93f5-401f-a0a1-af29daaddb79_1272x1974.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-cwp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0e151f-93f5-401f-a0a1-af29daaddb79_1272x1974.png" width="1272" height="1974" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ff0e151f-93f5-401f-a0a1-af29daaddb79_1272x1974.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1974,&quot;width&quot;:1272,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:223247,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0e151f-93f5-401f-a0a1-af29daaddb79_1272x1974.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-cwp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0e151f-93f5-401f-a0a1-af29daaddb79_1272x1974.png 424w, https://substackcdn.com/image/fetch/$s_!-cwp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0e151f-93f5-401f-a0a1-af29daaddb79_1272x1974.png 848w, https://substackcdn.com/image/fetch/$s_!-cwp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0e151f-93f5-401f-a0a1-af29daaddb79_1272x1974.png 1272w, https://substackcdn.com/image/fetch/$s_!-cwp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff0e151f-93f5-401f-a0a1-af29daaddb79_1272x1974.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">All code blocks are available in text format at the end of this article &#8226; #1</figcaption></figure></div><p>We&#8217;ll make significant changes to this code in this article, but bear with me for now.</p><p>A customer orders an invisibility potion:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QQSe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0494de-a2c1-4470-8dfc-92ed9314cd08_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!QQSe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0494de-a2c1-4470-8dfc-92ed9314cd08_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!QQSe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0494de-a2c1-4470-8dfc-92ed9314cd08_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!QQSe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0494de-a2c1-4470-8dfc-92ed9314cd08_1200x210.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QQSe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0494de-a2c1-4470-8dfc-92ed9314cd08_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5d0494de-a2c1-4470-8dfc-92ed9314cd08_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:16938,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0494de-a2c1-4470-8dfc-92ed9314cd08_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QQSe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0494de-a2c1-4470-8dfc-92ed9314cd08_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!QQSe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0494de-a2c1-4470-8dfc-92ed9314cd08_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!QQSe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0494de-a2c1-4470-8dfc-92ed9314cd08_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!QQSe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d0494de-a2c1-4470-8dfc-92ed9314cd08_1200x210.png 1456w" sizes="100vw"></picture><div></div></div></div><figcaption class="image-caption">#2</figcaption></figure></div><p>The output looks good:</p><pre><code>Invisibility  &#163;12.00
Owl dispatched to Merlin
12.00</code></pre><p>I&#8217;m using <code>Decimal</code> for the prices here as it&#8217;s a better data type for money than <code>float</code>. We discussed this a bit here, too: <a href="https://www.thepythoncodingstack.com/p/python-descriptors-the-price-is-right">The Weird and Wonderful World of Descriptors in Python &#8226; The Price is Right</a>.</p><p>The shop has sold a potion, updated its stock, recorded the sale, and notified the customer.</p><p>What could possibly go wrong?</p><h2><strong>A new label for the Moon Festival</strong></h2><p>The Moon Festival is approaching, and the shop owner wants special labels:</p><pre><code>&#127769; Moon Festival Invisibility &#127769;   &#163;12.00</code></pre><p>We could change <code>.sell()</code>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!31sQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8aa6ccd0-32d1-4d5f-9229-a2162a15cfd6_1200x1050.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!31sQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8aa6ccd0-32d1-4d5f-9229-a2162a15cfd6_1200x1050.png 424w, https://substackcdn.com/image/fetch/$s_!31sQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8aa6ccd0-32d1-4d5f-9229-a2162a15cfd6_1200x1050.png 848w, https://substackcdn.com/image/fetch/$s_!31sQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8aa6ccd0-32d1-4d5f-9229-a2162a15cfd6_1200x1050.png 1272w, https://substackcdn.com/image/fetch/$s_!31sQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8aa6ccd0-32d1-4d5f-9229-a2162a15cfd6_1200x1050.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!31sQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8aa6ccd0-32d1-4d5f-9229-a2162a15cfd6_1200x1050.png" width="1200" height="1050" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8aa6ccd0-32d1-4d5f-9229-a2162a15cfd6_1200x1050.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1050,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59952,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8aa6ccd0-32d1-4d5f-9229-a2162a15cfd6_1200x1050.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!31sQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8aa6ccd0-32d1-4d5f-9229-a2162a15cfd6_1200x1050.png 424w, https://substackcdn.com/image/fetch/$s_!31sQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8aa6ccd0-32d1-4d5f-9229-a2162a15cfd6_1200x1050.png 848w, https://substackcdn.com/image/fetch/$s_!31sQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8aa6ccd0-32d1-4d5f-9229-a2162a15cfd6_1200x1050.png 1272w, https://substackcdn.com/image/fetch/$s_!31sQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8aa6ccd0-32d1-4d5f-9229-a2162a15cfd6_1200x1050.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#3</figcaption></figure></div><p>You can read more about the &#8216;rogue asterisk&#8217; in the signature, a.k.a keyword-only arguments, in this post: <a href="https://www.thepythoncodingstack.com/p/python-function-parameters-arguments-args-kwargs-optional-positional-keyword">A Story About Parameters and Arguments in Python Functions &#8226; &#8220;AI Coffee&#8221; Grand Opening This Monday</a>.</p><p>Now this works:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tq_c!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaf8eb8c-47b7-4e64-b7a0-04ca1e29a34d_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!tq_c!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaf8eb8c-47b7-4e64-b7a0-04ca1e29a34d_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!tq_c!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaf8eb8c-47b7-4e64-b7a0-04ca1e29a34d_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!tq_c!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaf8eb8c-47b7-4e64-b7a0-04ca1e29a34d_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!tq_c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaf8eb8c-47b7-4e64-b7a0-04ca1e29a34d_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/daf8eb8c-47b7-4e64-b7a0-04ca1e29a34d_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:21470,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaf8eb8c-47b7-4e64-b7a0-04ca1e29a34d_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tq_c!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaf8eb8c-47b7-4e64-b7a0-04ca1e29a34d_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!tq_c!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaf8eb8c-47b7-4e64-b7a0-04ca1e29a34d_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!tq_c!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaf8eb8c-47b7-4e64-b7a0-04ca1e29a34d_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!tq_c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdaf8eb8c-47b7-4e64-b7a0-04ca1e29a34d_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#4</figcaption></figure></div><p>Here&#8217;s the output from this code:</p><pre><code>&#127769; Moon Festival Invisibility &#127769;  &#163;12.00
Owl dispatched to Merlin
12.00</code></pre><p>The change is small. Perhaps this class is still perfectly fine.</p><div><hr></div><p>The shop owner then asks for a test that checks the label without selling a potion. This is where you start to see the design problem.</p><p>At the moment, the only place that creates a label is inside <code>.sell()</code>. There is no suitable way to check the label policy independently. The code does not offer a way to ask for a label without also selling a potion.</p><p>The design problem is that <code>PotionShop</code> currently is in charge of label creation, even though the label policy can change independently of the policy that deals with sales. How about moving label creation to its own class?</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!f2ZF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ed22447-ec77-4a10-a4fe-7531fa85be96_1200x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!f2ZF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ed22447-ec77-4a10-a4fe-7531fa85be96_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!f2ZF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ed22447-ec77-4a10-a4fe-7531fa85be96_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!f2ZF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ed22447-ec77-4a10-a4fe-7531fa85be96_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!f2ZF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ed22447-ec77-4a10-a4fe-7531fa85be96_1200x630.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!f2ZF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ed22447-ec77-4a10-a4fe-7531fa85be96_1200x630.png" width="1200" height="630" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5ed22447-ec77-4a10-a4fe-7531fa85be96_1200x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:630,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:44323,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ed22447-ec77-4a10-a4fe-7531fa85be96_1200x630.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!f2ZF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ed22447-ec77-4a10-a4fe-7531fa85be96_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!f2ZF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ed22447-ec77-4a10-a4fe-7531fa85be96_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!f2ZF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ed22447-ec77-4a10-a4fe-7531fa85be96_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!f2ZF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ed22447-ec77-4a10-a4fe-7531fa85be96_1200x630.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#5</figcaption></figure></div><p>Now you can check that the label is correct without having to sell an item in the first place:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ui07!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F317910d4-bfd7-4862-a8a8-1bf950b82877_1200x336.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ui07!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F317910d4-bfd7-4862-a8a8-1bf950b82877_1200x336.png 424w, https://substackcdn.com/image/fetch/$s_!ui07!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F317910d4-bfd7-4862-a8a8-1bf950b82877_1200x336.png 848w, https://substackcdn.com/image/fetch/$s_!ui07!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F317910d4-bfd7-4862-a8a8-1bf950b82877_1200x336.png 1272w, https://substackcdn.com/image/fetch/$s_!ui07!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F317910d4-bfd7-4862-a8a8-1bf950b82877_1200x336.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ui07!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F317910d4-bfd7-4862-a8a8-1bf950b82877_1200x336.png" width="1200" height="336" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/317910d4-bfd7-4862-a8a8-1bf950b82877_1200x336.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:336,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29933,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F317910d4-bfd7-4862-a8a8-1bf950b82877_1200x336.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ui07!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F317910d4-bfd7-4862-a8a8-1bf950b82877_1200x336.png 424w, https://substackcdn.com/image/fetch/$s_!ui07!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F317910d4-bfd7-4862-a8a8-1bf950b82877_1200x336.png 848w, https://substackcdn.com/image/fetch/$s_!ui07!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F317910d4-bfd7-4862-a8a8-1bf950b82877_1200x336.png 1272w, https://substackcdn.com/image/fetch/$s_!ui07!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F317910d4-bfd7-4862-a8a8-1bf950b82877_1200x336.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#6</figcaption></figure></div><p>This code would raise an <code>AssertionError</code> if the string that <code>labels.make()</code> returns is not identical to the required output. In this case, there are no errors.</p><p>In a real project, you could use a testing platform to run these checks. But I&#8217;ll use plain <code>assert</code> statements throughout to keep things simpler in this tutorial.</p><p>The shop delegates label-making to this object:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BzDn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9681634-69f0-4bfb-bee2-518186d87ea8_1200x1134.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BzDn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9681634-69f0-4bfb-bee2-518186d87ea8_1200x1134.png 424w, https://substackcdn.com/image/fetch/$s_!BzDn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9681634-69f0-4bfb-bee2-518186d87ea8_1200x1134.png 848w, https://substackcdn.com/image/fetch/$s_!BzDn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9681634-69f0-4bfb-bee2-518186d87ea8_1200x1134.png 1272w, https://substackcdn.com/image/fetch/$s_!BzDn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9681634-69f0-4bfb-bee2-518186d87ea8_1200x1134.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!BzDn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9681634-69f0-4bfb-bee2-518186d87ea8_1200x1134.png" width="1200" height="1134" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e9681634-69f0-4bfb-bee2-518186d87ea8_1200x1134.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1134,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:56959,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9681634-69f0-4bfb-bee2-518186d87ea8_1200x1134.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!BzDn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9681634-69f0-4bfb-bee2-518186d87ea8_1200x1134.png 424w, https://substackcdn.com/image/fetch/$s_!BzDn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9681634-69f0-4bfb-bee2-518186d87ea8_1200x1134.png 848w, https://substackcdn.com/image/fetch/$s_!BzDn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9681634-69f0-4bfb-bee2-518186d87ea8_1200x1134.png 1272w, https://substackcdn.com/image/fetch/$s_!BzDn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9681634-69f0-4bfb-bee2-518186d87ea8_1200x1134.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#7</figcaption></figure></div><p>You should also remove the three lines that create a label you had further down within <code>.sell()</code>.</p><p>We&#8217;re using <em>composition</em> here. The Potion Shop <em>has a</em> label maker, and you pass a <code>LabelMaker</code> object to the <code>.label_maker</code> attribute in the <code>PotionShop</code> object. The <code>PotionShop</code> object can now delegate label making to an object dedicated to this task.</p><p>From this point onwards, the shop receives the label-making collaborator:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1U0D!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d8da646-9053-4d9d-88b2-685633a01914_1200x168.png 424w, https://substackcdn.com/image/fetch/$s_!1U0D!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d8da646-9053-4d9d-88b2-685633a01914_1200x168.png 848w, https://substackcdn.com/image/fetch/$s_!1U0D!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d8da646-9053-4d9d-88b2-685633a01914_1200x168.png 1272w, https://substackcdn.com/image/fetch/$s_!1U0D!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d8da646-9053-4d9d-88b2-685633a01914_1200x168.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!1U0D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d8da646-9053-4d9d-88b2-685633a01914_1200x168.png" width="1200" height="168" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6d8da646-9053-4d9d-88b2-685633a01914_1200x168.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:168,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9915,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d8da646-9053-4d9d-88b2-685633a01914_1200x168.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1U0D!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d8da646-9053-4d9d-88b2-685633a01914_1200x168.png 424w, https://substackcdn.com/image/fetch/$s_!1U0D!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d8da646-9053-4d9d-88b2-685633a01914_1200x168.png 848w, https://substackcdn.com/image/fetch/$s_!1U0D!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d8da646-9053-4d9d-88b2-685633a01914_1200x168.png 1272w, https://substackcdn.com/image/fetch/$s_!1U0D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d8da646-9053-4d9d-88b2-685633a01914_1200x168.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#8</figcaption></figure></div><p><code>PotionShop</code> still coordinates the sale, but the label policy can now change without changing <code>PotionShop</code>. We started separating responsibilities. Making labels is no longer the responsibility of the <code>PotionShop</code> object. But we still have a bit more to go.</p><div><hr></div><p>You got a taste of what the <em>Single Responsibility Principle</em> does in the example so far. But the code still has other transgressions of this principle. Let&#8217;s work through them in the rest of this post.</p><div class="pullquote"><p><em><strong>Coming soon:</strong> Let&#8217;s build a project from beginning to end in collaboration with AI agents. This will NOT be vibe coding. Instead, it&#8217;s a collaboration between knowledgeable humans and powerful AI.</em></p><p><em>This is the direction real programming is taking.</em></p><p><em>And we&#8217;ll explore SOLID principles in this project, which I&#8217;ll share as text and videos. Make sure you&#8217;re a premium subscriber so you don&#8217;t miss out.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p></div><h2><strong>The owls go on strike</strong></h2><p>A few days later, the owls refuse to fly during a thunderstorm. The potions can still be prepared, but the notification service is unavailable.</p><p>The shop needs to stop sending notifications temporarily.</p><p>Before changing the notification system, we first make the owl&#8217;s availability explicit:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ywHS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a036ca-c203-4a68-ae74-0e2e3f11c480_1200x1134.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ywHS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a036ca-c203-4a68-ae74-0e2e3f11c480_1200x1134.png 424w, https://substackcdn.com/image/fetch/$s_!ywHS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a036ca-c203-4a68-ae74-0e2e3f11c480_1200x1134.png 848w, https://substackcdn.com/image/fetch/$s_!ywHS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a036ca-c203-4a68-ae74-0e2e3f11c480_1200x1134.png 1272w, https://substackcdn.com/image/fetch/$s_!ywHS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a036ca-c203-4a68-ae74-0e2e3f11c480_1200x1134.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ywHS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a036ca-c203-4a68-ae74-0e2e3f11c480_1200x1134.png" width="1200" height="1134" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/24a036ca-c203-4a68-ae74-0e2e3f11c480_1200x1134.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1134,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:91432,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a036ca-c203-4a68-ae74-0e2e3f11c480_1200x1134.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ywHS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a036ca-c203-4a68-ae74-0e2e3f11c480_1200x1134.png 424w, https://substackcdn.com/image/fetch/$s_!ywHS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a036ca-c203-4a68-ae74-0e2e3f11c480_1200x1134.png 848w, https://substackcdn.com/image/fetch/$s_!ywHS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a036ca-c203-4a68-ae74-0e2e3f11c480_1200x1134.png 1272w, https://substackcdn.com/image/fetch/$s_!ywHS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a036ca-c203-4a68-ae74-0e2e3f11c480_1200x1134.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#9</figcaption></figure></div><p>But you also want to add a test to check the price is correct.</p><p>Your code extracts the price from the <code>.recipes</code> class attribute as part of the <code>.sell()</code> method. So, this is how you can test the price is what you&#8217;re expecting:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6AgR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1a86c54-02c7-4d99-87c1-06bd5da51a9f_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!6AgR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1a86c54-02c7-4d99-87c1-06bd5da51a9f_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!6AgR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1a86c54-02c7-4d99-87c1-06bd5da51a9f_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!6AgR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1a86c54-02c7-4d99-87c1-06bd5da51a9f_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!6AgR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1a86c54-02c7-4d99-87c1-06bd5da51a9f_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c1a86c54-02c7-4d99-87c1-06bd5da51a9f_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24495,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1a86c54-02c7-4d99-87c1-06bd5da51a9f_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6AgR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1a86c54-02c7-4d99-87c1-06bd5da51a9f_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!6AgR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1a86c54-02c7-4d99-87c1-06bd5da51a9f_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!6AgR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1a86c54-02c7-4d99-87c1-06bd5da51a9f_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!6AgR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1a86c54-02c7-4d99-87c1-06bd5da51a9f_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#10</figcaption></figure></div><p>You get the following output and no <code>AssertionError</code> in this case:</p><pre><code>Invisibility  &#163;12.00
Owl dispatched to Merlin</code></pre><p>However, you should also test this when the owls are unavailable. Of course, this shouldn&#8217;t make any difference to the price:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aEei!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5beef4-7fd7-4d40-afef-227762dab2fc_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!aEei!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5beef4-7fd7-4d40-afef-227762dab2fc_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!aEei!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5beef4-7fd7-4d40-afef-227762dab2fc_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!aEei!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5beef4-7fd7-4d40-afef-227762dab2fc_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!aEei!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5beef4-7fd7-4d40-afef-227762dab2fc_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ef5beef4-7fd7-4d40-afef-227762dab2fc_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:27426,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5beef4-7fd7-4d40-afef-227762dab2fc_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!aEei!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5beef4-7fd7-4d40-afef-227762dab2fc_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!aEei!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5beef4-7fd7-4d40-afef-227762dab2fc_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!aEei!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5beef4-7fd7-4d40-afef-227762dab2fc_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!aEei!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5beef4-7fd7-4d40-afef-227762dab2fc_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#11</figcaption></figure></div><p>But, your code doesn&#8217;t get that far. The <code>.sell()</code> method calculates the price and prints the label. But the owl service raises <code>OwlUnavailable</code> and the code stops before you can verify the price is right:</p><pre><code>Invisibility  &#163;12.00
&#8203;
Traceback (most recent call last):
  ...
OwlUnavailable: The owls are currently unavailable</code></pre><p>The price is not the problem. The test cannot inspect the price without also triggering an owl notification.</p><p>The shop&#8217;s different jobs are now getting in each other&#8217;s way. But before we fix this problem, let&#8217;s make the mess bigger.</p><h2><strong>A discount for cauldron owners</strong></h2><p>The owner has another idea. Customers who bring their own cauldron should receive a 10% discount.</p><p>We add another parameter:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ibQ4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc0b67fc-59fc-4cd9-a6be-76544d9b83c5_1362x1092.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ibQ4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc0b67fc-59fc-4cd9-a6be-76544d9b83c5_1362x1092.png 424w, https://substackcdn.com/image/fetch/$s_!ibQ4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc0b67fc-59fc-4cd9-a6be-76544d9b83c5_1362x1092.png 848w, https://substackcdn.com/image/fetch/$s_!ibQ4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc0b67fc-59fc-4cd9-a6be-76544d9b83c5_1362x1092.png 1272w, https://substackcdn.com/image/fetch/$s_!ibQ4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc0b67fc-59fc-4cd9-a6be-76544d9b83c5_1362x1092.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ibQ4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc0b67fc-59fc-4cd9-a6be-76544d9b83c5_1362x1092.png" width="1362" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fc0b67fc-59fc-4cd9-a6be-76544d9b83c5_1362x1092.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1362,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59173,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc0b67fc-59fc-4cd9-a6be-76544d9b83c5_1362x1092.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ibQ4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc0b67fc-59fc-4cd9-a6be-76544d9b83c5_1362x1092.png 424w, https://substackcdn.com/image/fetch/$s_!ibQ4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc0b67fc-59fc-4cd9-a6be-76544d9b83c5_1362x1092.png 848w, https://substackcdn.com/image/fetch/$s_!ibQ4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc0b67fc-59fc-4cd9-a6be-76544d9b83c5_1362x1092.png 1272w, https://substackcdn.com/image/fetch/$s_!ibQ4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc0b67fc-59fc-4cd9-a6be-76544d9b83c5_1362x1092.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#12</figcaption></figure></div><p>You use <code>.quantize()</code>, which is a method in the <code>Decimal</code> class, to ensure the discounted value is rounded to two decimal places. Let&#8217;s try this with a shop whose notification service is available:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sdIw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7564fd2-685f-4bfe-93d4-c6439c3dc628_1200x462.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sdIw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7564fd2-685f-4bfe-93d4-c6439c3dc628_1200x462.png 424w, https://substackcdn.com/image/fetch/$s_!sdIw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7564fd2-685f-4bfe-93d4-c6439c3dc628_1200x462.png 848w, https://substackcdn.com/image/fetch/$s_!sdIw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7564fd2-685f-4bfe-93d4-c6439c3dc628_1200x462.png 1272w, https://substackcdn.com/image/fetch/$s_!sdIw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7564fd2-685f-4bfe-93d4-c6439c3dc628_1200x462.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!sdIw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7564fd2-685f-4bfe-93d4-c6439c3dc628_1200x462.png" width="1200" height="462" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e7564fd2-685f-4bfe-93d4-c6439c3dc628_1200x462.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:462,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28783,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7564fd2-685f-4bfe-93d4-c6439c3dc628_1200x462.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sdIw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7564fd2-685f-4bfe-93d4-c6439c3dc628_1200x462.png 424w, https://substackcdn.com/image/fetch/$s_!sdIw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7564fd2-685f-4bfe-93d4-c6439c3dc628_1200x462.png 848w, https://substackcdn.com/image/fetch/$s_!sdIw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7564fd2-685f-4bfe-93d4-c6439c3dc628_1200x462.png 1272w, https://substackcdn.com/image/fetch/$s_!sdIw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7564fd2-685f-4bfe-93d4-c6439c3dc628_1200x462.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#13</figcaption></figure></div><p>The output shows the correct discounted price:</p><pre><code>Invisibility  &#163;10.80
Owl dispatched to Merlin
10.80</code></pre><p>Let&#8217;s see what happens if the owls are unavailable:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Dw6w!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5337853c-0ff3-4642-872e-6b731f750e26_1200x462.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Dw6w!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5337853c-0ff3-4642-872e-6b731f750e26_1200x462.png 424w, https://substackcdn.com/image/fetch/$s_!Dw6w!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5337853c-0ff3-4642-872e-6b731f750e26_1200x462.png 848w, https://substackcdn.com/image/fetch/$s_!Dw6w!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5337853c-0ff3-4642-872e-6b731f750e26_1200x462.png 1272w, https://substackcdn.com/image/fetch/$s_!Dw6w!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5337853c-0ff3-4642-872e-6b731f750e26_1200x462.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Dw6w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5337853c-0ff3-4642-872e-6b731f750e26_1200x462.png" width="1200" height="462" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5337853c-0ff3-4642-872e-6b731f750e26_1200x462.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:462,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31478,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5337853c-0ff3-4642-872e-6b731f750e26_1200x462.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Dw6w!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5337853c-0ff3-4642-872e-6b731f750e26_1200x462.png 424w, https://substackcdn.com/image/fetch/$s_!Dw6w!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5337853c-0ff3-4642-872e-6b731f750e26_1200x462.png 848w, https://substackcdn.com/image/fetch/$s_!Dw6w!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5337853c-0ff3-4642-872e-6b731f750e26_1200x462.png 1272w, https://substackcdn.com/image/fetch/$s_!Dw6w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5337853c-0ff3-4642-872e-6b731f750e26_1200x462.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#14</figcaption></figure></div><p>Your code raises an error since the owls aren&#8217;t flying:</p><pre><code>Invisibility  &#163;10.80
Traceback (most recent call last):
  ...
OwlUnavailable: The owls are currently unavailable</code></pre><p>But there&#8217;s an even bigger problem now. Can you spot it?</p><p>Even though the code stopped execution and raised an error, it had already changed the stock and recorded a sale. Let&#8217;s use a <code>try..except</code> block to see what&#8217;s happening:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pZlb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6644562f-b86e-47d0-bde4-617c8353062b_1200x672.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pZlb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6644562f-b86e-47d0-bde4-617c8353062b_1200x672.png 424w, https://substackcdn.com/image/fetch/$s_!pZlb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6644562f-b86e-47d0-bde4-617c8353062b_1200x672.png 848w, https://substackcdn.com/image/fetch/$s_!pZlb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6644562f-b86e-47d0-bde4-617c8353062b_1200x672.png 1272w, https://substackcdn.com/image/fetch/$s_!pZlb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6644562f-b86e-47d0-bde4-617c8353062b_1200x672.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!pZlb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6644562f-b86e-47d0-bde4-617c8353062b_1200x672.png" width="1200" height="672" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6644562f-b86e-47d0-bde4-617c8353062b_1200x672.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:672,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:65045,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6644562f-b86e-47d0-bde4-617c8353062b_1200x672.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pZlb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6644562f-b86e-47d0-bde4-617c8353062b_1200x672.png 424w, https://substackcdn.com/image/fetch/$s_!pZlb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6644562f-b86e-47d0-bde4-617c8353062b_1200x672.png 848w, https://substackcdn.com/image/fetch/$s_!pZlb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6644562f-b86e-47d0-bde4-617c8353062b_1200x672.png 1272w, https://substackcdn.com/image/fetch/$s_!pZlb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6644562f-b86e-47d0-bde4-617c8353062b_1200x672.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#15</figcaption></figure></div><p>Here&#8217;s the output:</p><pre><code>Invisibility  &#163;10.80
Catching the &#8216;OwlUnavailable&#8217; Exception
{&#8217;moonflower&#8217;: 8, &#8216;dragon_scale&#8217;: 0}
[(&#8217;Merlin&#8217;, &#8216;invisibility&#8217;, Decimal(&#8217;10.80&#8217;))]</code></pre><p>You started off with 10 units of moonflower and 2 units of dragon scales in the potion shop. These values are currently hardcoded in the class&#8217;s <code>.__init__()</code> special method (which is in itself not a good idea, but more on this later.)</p><p>The recipe uses two units of each, as shown in the <code>.recipes</code> class attribute (which will also change soon.)</p><p>So you end up using up the stock even though <code>.sell()</code> raised an exception! You can see from the output that the sale was also registered.</p><h2><strong>One failure too many</strong></h2><p>The <code>.sell()</code> method is now responsible for:</p><ul><li><p>Looking up recipes</p></li><li><p>Removing ingredients</p></li><li><p>Calculating prices</p></li><li><p>Applying discounts</p></li><li><p>Coordinating label creation*</p></li><li><p>Recording sales</p></li><li><p>Sending notifications</p></li></ul><p>* Creating labels is the one we have already improved. It&#8217;s the others that are still a problem!</p><p>And the owner has just mentioned loyalty points, premium labels, email notifications, and a special discount for customers who arrive by broomstick.</p><p>The <code>.sell()</code> method is becoming the place where every new request arrives.</p><p>And it&#8217;s not just <code>PotionShop.sell()</code> that&#8217;s trying to do too much. The <code>PotionShop</code> class also takes care of the recipes available in the <code>.recipes</code> class attribute and the stock available in the shop.</p><p>It is time to separate these responsibilities.</p><h2><strong>Separating the recipe book</strong></h2><p>Let&#8217;s start with the recipes. The shop should not need to know the details of every recipe. That is the recipe book&#8217;s job:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HRcc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facf26af5-53df-45d7-843c-7d3ac275be85_1200x714.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HRcc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facf26af5-53df-45d7-843c-7d3ac275be85_1200x714.png 424w, https://substackcdn.com/image/fetch/$s_!HRcc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facf26af5-53df-45d7-843c-7d3ac275be85_1200x714.png 848w, https://substackcdn.com/image/fetch/$s_!HRcc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facf26af5-53df-45d7-843c-7d3ac275be85_1200x714.png 1272w, https://substackcdn.com/image/fetch/$s_!HRcc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facf26af5-53df-45d7-843c-7d3ac275be85_1200x714.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!HRcc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facf26af5-53df-45d7-843c-7d3ac275be85_1200x714.png" width="1200" height="714" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/acf26af5-53df-45d7-843c-7d3ac275be85_1200x714.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:714,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:62038,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facf26af5-53df-45d7-843c-7d3ac275be85_1200x714.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HRcc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facf26af5-53df-45d7-843c-7d3ac275be85_1200x714.png 424w, https://substackcdn.com/image/fetch/$s_!HRcc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facf26af5-53df-45d7-843c-7d3ac275be85_1200x714.png 848w, https://substackcdn.com/image/fetch/$s_!HRcc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facf26af5-53df-45d7-843c-7d3ac275be85_1200x714.png 1272w, https://substackcdn.com/image/fetch/$s_!HRcc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facf26af5-53df-45d7-843c-7d3ac275be85_1200x714.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#16</figcaption></figure></div><p>This class is responsible for the recipe book. And nothing else. You add the <code>.find()</code> method to make it easier for a user to use the class without knowing the details of how it&#8217;s built. It&#8217;s usually best for a class to manage its own data. So, rather than requiring the user to know that the recipes are stored in a dictionary, you provide the <code>.find()</code> method that does the work.</p><p>Suppose the potion maker changes a recipe or adds a new potion. Those changes belong in <code>RecipeBook</code>; the steps for selling a potion don&#8217;t need to change.</p><p>We can test this class without creating a shop, changing stock, printing labels, or sending owls:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wfR5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe80f6283-149d-4af8-bcd0-7baa9e2905ee_1308x210.png 424w, https://substackcdn.com/image/fetch/$s_!wfR5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe80f6283-149d-4af8-bcd0-7baa9e2905ee_1308x210.png 848w, https://substackcdn.com/image/fetch/$s_!wfR5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe80f6283-149d-4af8-bcd0-7baa9e2905ee_1308x210.png 1272w, https://substackcdn.com/image/fetch/$s_!wfR5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe80f6283-149d-4af8-bcd0-7baa9e2905ee_1308x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!wfR5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe80f6283-149d-4af8-bcd0-7baa9e2905ee_1308x210.png" width="1308" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e80f6283-149d-4af8-bcd0-7baa9e2905ee_1308x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1308,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:23471,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe80f6283-149d-4af8-bcd0-7baa9e2905ee_1308x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wfR5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe80f6283-149d-4af8-bcd0-7baa9e2905ee_1308x210.png 424w, https://substackcdn.com/image/fetch/$s_!wfR5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe80f6283-149d-4af8-bcd0-7baa9e2905ee_1308x210.png 848w, https://substackcdn.com/image/fetch/$s_!wfR5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe80f6283-149d-4af8-bcd0-7baa9e2905ee_1308x210.png 1272w, https://substackcdn.com/image/fetch/$s_!wfR5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe80f6283-149d-4af8-bcd0-7baa9e2905ee_1308x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#17</figcaption></figure></div><p>This code doesn&#8217;t raise an exception since the price is correct.</p><h2><strong>Separating inventory</strong></h2><p>Inventory management also doesn&#8217;t belong to the <code>PotionShop</code> class:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EpzM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf29f6b0-9f20-4050-ad34-e5d17e0d5c01_1470x756.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EpzM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf29f6b0-9f20-4050-ad34-e5d17e0d5c01_1470x756.png 424w, https://substackcdn.com/image/fetch/$s_!EpzM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf29f6b0-9f20-4050-ad34-e5d17e0d5c01_1470x756.png 848w, https://substackcdn.com/image/fetch/$s_!EpzM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf29f6b0-9f20-4050-ad34-e5d17e0d5c01_1470x756.png 1272w, https://substackcdn.com/image/fetch/$s_!EpzM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf29f6b0-9f20-4050-ad34-e5d17e0d5c01_1470x756.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!EpzM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf29f6b0-9f20-4050-ad34-e5d17e0d5c01_1470x756.png" width="1456" height="749" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bf29f6b0-9f20-4050-ad34-e5d17e0d5c01_1470x756.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:749,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:107051,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf29f6b0-9f20-4050-ad34-e5d17e0d5c01_1470x756.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!EpzM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf29f6b0-9f20-4050-ad34-e5d17e0d5c01_1470x756.png 424w, https://substackcdn.com/image/fetch/$s_!EpzM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf29f6b0-9f20-4050-ad34-e5d17e0d5c01_1470x756.png 848w, https://substackcdn.com/image/fetch/$s_!EpzM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf29f6b0-9f20-4050-ad34-e5d17e0d5c01_1470x756.png 1272w, https://substackcdn.com/image/fetch/$s_!EpzM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf29f6b0-9f20-4050-ad34-e5d17e0d5c01_1470x756.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#18</figcaption></figure></div><p>The two loops in <code>.remove_stock()</code> are deliberate. If we reduced each quantity as we went, we could use some ingredients and then discover that there isn&#8217;t enough of another. The sale would fail, but the stock would already be partly changed. Checking every quantity first avoids that partial update.</p><p>We can see what happens when there isn&#8217;t enough stock:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YB2b!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcab5d3f7-32dc-47d0-8d8c-bb4876d4c4cd_1200x882.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YB2b!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcab5d3f7-32dc-47d0-8d8c-bb4876d4c4cd_1200x882.png 424w, https://substackcdn.com/image/fetch/$s_!YB2b!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcab5d3f7-32dc-47d0-8d8c-bb4876d4c4cd_1200x882.png 848w, https://substackcdn.com/image/fetch/$s_!YB2b!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcab5d3f7-32dc-47d0-8d8c-bb4876d4c4cd_1200x882.png 1272w, https://substackcdn.com/image/fetch/$s_!YB2b!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcab5d3f7-32dc-47d0-8d8c-bb4876d4c4cd_1200x882.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!YB2b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcab5d3f7-32dc-47d0-8d8c-bb4876d4c4cd_1200x882.png" width="1200" height="882" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cab5d3f7-32dc-47d0-8d8c-bb4876d4c4cd_1200x882.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:882,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59029,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcab5d3f7-32dc-47d0-8d8c-bb4876d4c4cd_1200x882.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!YB2b!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcab5d3f7-32dc-47d0-8d8c-bb4876d4c4cd_1200x882.png 424w, https://substackcdn.com/image/fetch/$s_!YB2b!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcab5d3f7-32dc-47d0-8d8c-bb4876d4c4cd_1200x882.png 848w, https://substackcdn.com/image/fetch/$s_!YB2b!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcab5d3f7-32dc-47d0-8d8c-bb4876d4c4cd_1200x882.png 1272w, https://substackcdn.com/image/fetch/$s_!YB2b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcab5d3f7-32dc-47d0-8d8c-bb4876d4c4cd_1200x882.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#19</figcaption></figure></div><p>The operation fails before changing any stock:</p><pre><code>Not enough dragon_scale
{&#8217;moonflower&#8217;: 10, &#8216;dragon_scale&#8217;: 2}</code></pre><p>After a successful sale, a delivery can add stock again:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!s9HI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd3e641-3a13-4a8f-8e9f-7a2b57d89040_1200x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!s9HI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd3e641-3a13-4a8f-8e9f-7a2b57d89040_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!s9HI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd3e641-3a13-4a8f-8e9f-7a2b57d89040_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!s9HI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd3e641-3a13-4a8f-8e9f-7a2b57d89040_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!s9HI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd3e641-3a13-4a8f-8e9f-7a2b57d89040_1200x630.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!s9HI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd3e641-3a13-4a8f-8e9f-7a2b57d89040_1200x630.png" width="1200" height="630" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dfd3e641-3a13-4a8f-8e9f-7a2b57d89040_1200x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:630,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:40617,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd3e641-3a13-4a8f-8e9f-7a2b57d89040_1200x630.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!s9HI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd3e641-3a13-4a8f-8e9f-7a2b57d89040_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!s9HI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd3e641-3a13-4a8f-8e9f-7a2b57d89040_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!s9HI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd3e641-3a13-4a8f-8e9f-7a2b57d89040_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!s9HI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd3e641-3a13-4a8f-8e9f-7a2b57d89040_1200x630.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#20</figcaption></figure></div><p>Here&#8217;s the output from this code:</p><pre><code>{&#8217;moonflower&#8217;: 13, &#8216;dragon_scale&#8217;: 0}</code></pre><p><code>Inventory</code> owns both the stock data and the operations that change its quantities. It does not need to know who bought the potion or whether the label is festive.</p><h2><strong>Separating prices and labels</strong></h2><p>Pricing has its own rules:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Eyj_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b369b2-2630-4406-8f3b-c6b8457415f4_1200x546.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Eyj_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b369b2-2630-4406-8f3b-c6b8457415f4_1200x546.png 424w, https://substackcdn.com/image/fetch/$s_!Eyj_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b369b2-2630-4406-8f3b-c6b8457415f4_1200x546.png 848w, https://substackcdn.com/image/fetch/$s_!Eyj_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b369b2-2630-4406-8f3b-c6b8457415f4_1200x546.png 1272w, https://substackcdn.com/image/fetch/$s_!Eyj_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b369b2-2630-4406-8f3b-c6b8457415f4_1200x546.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Eyj_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b369b2-2630-4406-8f3b-c6b8457415f4_1200x546.png" width="1200" height="546" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d9b369b2-2630-4406-8f3b-c6b8457415f4_1200x546.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:546,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:50656,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b369b2-2630-4406-8f3b-c6b8457415f4_1200x546.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Eyj_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b369b2-2630-4406-8f3b-c6b8457415f4_1200x546.png 424w, https://substackcdn.com/image/fetch/$s_!Eyj_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b369b2-2630-4406-8f3b-c6b8457415f4_1200x546.png 848w, https://substackcdn.com/image/fetch/$s_!Eyj_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b369b2-2630-4406-8f3b-c6b8457415f4_1200x546.png 1272w, https://substackcdn.com/image/fetch/$s_!Eyj_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9b369b2-2630-4406-8f3b-c6b8457415f4_1200x546.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#21</figcaption></figure></div><p>Once again, this class now has just one responsibility: to deal with the pricing.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!n5HU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6cd761b-90fb-435c-8180-34a4bafb4d8c_1200x420.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!n5HU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6cd761b-90fb-435c-8180-34a4bafb4d8c_1200x420.png 424w, https://substackcdn.com/image/fetch/$s_!n5HU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6cd761b-90fb-435c-8180-34a4bafb4d8c_1200x420.png 848w, https://substackcdn.com/image/fetch/$s_!n5HU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6cd761b-90fb-435c-8180-34a4bafb4d8c_1200x420.png 1272w, https://substackcdn.com/image/fetch/$s_!n5HU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6cd761b-90fb-435c-8180-34a4bafb4d8c_1200x420.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!n5HU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6cd761b-90fb-435c-8180-34a4bafb4d8c_1200x420.png" width="1200" height="420" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e6cd761b-90fb-435c-8180-34a4bafb4d8c_1200x420.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:420,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31661,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6cd761b-90fb-435c-8180-34a4bafb4d8c_1200x420.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!n5HU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6cd761b-90fb-435c-8180-34a4bafb4d8c_1200x420.png 424w, https://substackcdn.com/image/fetch/$s_!n5HU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6cd761b-90fb-435c-8180-34a4bafb4d8c_1200x420.png 848w, https://substackcdn.com/image/fetch/$s_!n5HU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6cd761b-90fb-435c-8180-34a4bafb4d8c_1200x420.png 1272w, https://substackcdn.com/image/fetch/$s_!n5HU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6cd761b-90fb-435c-8180-34a4bafb4d8c_1200x420.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#22</figcaption></figure></div><p>You can check the pricing strategy works without having to sell a product and everything else.</p><pre><code>10.80</code></pre><p>Labels have their own rules too. You dealt with this earlier in the tutorial when you created <code>LabelMaker</code></p><p><code>PriceCalculator</code> and <code>LabelMaker</code> could both be functions here. We&#8217;re using classes because they act as collaborators that the shop receives from outside. Later, we&#8217;ll see how protocols or abstract base classes can describe interchangeable collaborators more explicitly. For now, the important point is the separation of responsibilities and not the fact that each responsibility has become a class.</p><p>Neither class needs an inventory, a sales ledger, or an owl.</p><h2><strong>Putting the shop back together</strong></h2><p>We still need something to coordinate the sale. First, we give the sales ledger and the notifier small, focused interfaces:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!w-Av!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed530fd-6b12-4d0e-bf0e-ea09410a5101_1200x966.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!w-Av!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed530fd-6b12-4d0e-bf0e-ea09410a5101_1200x966.png 424w, https://substackcdn.com/image/fetch/$s_!w-Av!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed530fd-6b12-4d0e-bf0e-ea09410a5101_1200x966.png 848w, https://substackcdn.com/image/fetch/$s_!w-Av!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed530fd-6b12-4d0e-bf0e-ea09410a5101_1200x966.png 1272w, https://substackcdn.com/image/fetch/$s_!w-Av!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed530fd-6b12-4d0e-bf0e-ea09410a5101_1200x966.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!w-Av!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed530fd-6b12-4d0e-bf0e-ea09410a5101_1200x966.png" width="1200" height="966" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3ed530fd-6b12-4d0e-bf0e-ea09410a5101_1200x966.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:966,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:104177,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed530fd-6b12-4d0e-bf0e-ea09410a5101_1200x966.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!w-Av!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed530fd-6b12-4d0e-bf0e-ea09410a5101_1200x966.png 424w, https://substackcdn.com/image/fetch/$s_!w-Av!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed530fd-6b12-4d0e-bf0e-ea09410a5101_1200x966.png 848w, https://substackcdn.com/image/fetch/$s_!w-Av!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed530fd-6b12-4d0e-bf0e-ea09410a5101_1200x966.png 1272w, https://substackcdn.com/image/fetch/$s_!w-Av!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed530fd-6b12-4d0e-bf0e-ea09410a5101_1200x966.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#23</figcaption></figure></div><p>Notice the <code>"pending"</code> state in the ledger. Separating responsibilities does not make the sale process independent of anything else. Inventory has already changed before later steps run. Here, the narrower goal is to make notification failure visible so that the recorded sale can be investigated.</p><p>Now we can keep <code>PotionShop</code> as a thin coordinator. It still represents the sale process, but it delegates each part of that process to the object that owns the relevant behaviour:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FlUh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16815ce0-0924-4d23-a6ae-9e95af69305b_1254x1680.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FlUh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16815ce0-0924-4d23-a6ae-9e95af69305b_1254x1680.png 424w, https://substackcdn.com/image/fetch/$s_!FlUh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16815ce0-0924-4d23-a6ae-9e95af69305b_1254x1680.png 848w, https://substackcdn.com/image/fetch/$s_!FlUh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16815ce0-0924-4d23-a6ae-9e95af69305b_1254x1680.png 1272w, https://substackcdn.com/image/fetch/$s_!FlUh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16815ce0-0924-4d23-a6ae-9e95af69305b_1254x1680.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!FlUh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16815ce0-0924-4d23-a6ae-9e95af69305b_1254x1680.png" width="1254" height="1680" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/16815ce0-0924-4d23-a6ae-9e95af69305b_1254x1680.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1680,&quot;width&quot;:1254,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:174356,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16815ce0-0924-4d23-a6ae-9e95af69305b_1254x1680.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FlUh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16815ce0-0924-4d23-a6ae-9e95af69305b_1254x1680.png 424w, https://substackcdn.com/image/fetch/$s_!FlUh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16815ce0-0924-4d23-a6ae-9e95af69305b_1254x1680.png 848w, https://substackcdn.com/image/fetch/$s_!FlUh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16815ce0-0924-4d23-a6ae-9e95af69305b_1254x1680.png 1272w, https://substackcdn.com/image/fetch/$s_!FlUh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16815ce0-0924-4d23-a6ae-9e95af69305b_1254x1680.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#24</figcaption></figure></div><p>We can now assemble the shop and sell a potion:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!g4Hl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b29ad29-0a9e-4e3d-aa41-e28050629c6c_1200x1008.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!g4Hl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b29ad29-0a9e-4e3d-aa41-e28050629c6c_1200x1008.png 424w, https://substackcdn.com/image/fetch/$s_!g4Hl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b29ad29-0a9e-4e3d-aa41-e28050629c6c_1200x1008.png 848w, https://substackcdn.com/image/fetch/$s_!g4Hl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b29ad29-0a9e-4e3d-aa41-e28050629c6c_1200x1008.png 1272w, https://substackcdn.com/image/fetch/$s_!g4Hl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b29ad29-0a9e-4e3d-aa41-e28050629c6c_1200x1008.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!g4Hl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b29ad29-0a9e-4e3d-aa41-e28050629c6c_1200x1008.png" width="1200" height="1008" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6b29ad29-0a9e-4e3d-aa41-e28050629c6c_1200x1008.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1008,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:89560,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b29ad29-0a9e-4e3d-aa41-e28050629c6c_1200x1008.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!g4Hl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b29ad29-0a9e-4e3d-aa41-e28050629c6c_1200x1008.png 424w, https://substackcdn.com/image/fetch/$s_!g4Hl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b29ad29-0a9e-4e3d-aa41-e28050629c6c_1200x1008.png 848w, https://substackcdn.com/image/fetch/$s_!g4Hl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b29ad29-0a9e-4e3d-aa41-e28050629c6c_1200x1008.png 1272w, https://substackcdn.com/image/fetch/$s_!g4Hl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b29ad29-0a9e-4e3d-aa41-e28050629c6c_1200x1008.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#25</figcaption></figure></div><p>Here&#8217;s the output:</p><pre><code>&#127769; Moon Festival Invisibility &#127769; &#163;10.80
Owl dispatched to Merlin
10.80
{&#8217;customer&#8217;: &#8216;Merlin&#8217;, &#8216;potion&#8217;: &#8216;invisibility&#8217;, &#8216;price&#8217;: Decimal(&#8217;10.80&#8217;), &#8216;notification&#8217;: &#8216;sent&#8217;}</code></pre><p>If notification fails after the ledger records the sale, the sale remains<br>visible with a pending notification:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FPq9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9df1a3-caa7-46a3-accb-e566eefa5157_1272x1008.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FPq9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9df1a3-caa7-46a3-accb-e566eefa5157_1272x1008.png 424w, https://substackcdn.com/image/fetch/$s_!FPq9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9df1a3-caa7-46a3-accb-e566eefa5157_1272x1008.png 848w, https://substackcdn.com/image/fetch/$s_!FPq9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9df1a3-caa7-46a3-accb-e566eefa5157_1272x1008.png 1272w, https://substackcdn.com/image/fetch/$s_!FPq9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9df1a3-caa7-46a3-accb-e566eefa5157_1272x1008.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!FPq9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9df1a3-caa7-46a3-accb-e566eefa5157_1272x1008.png" width="1272" height="1008" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fc9df1a3-caa7-46a3-accb-e566eefa5157_1272x1008.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1008,&quot;width&quot;:1272,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:117521,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9df1a3-caa7-46a3-accb-e566eefa5157_1272x1008.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FPq9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9df1a3-caa7-46a3-accb-e566eefa5157_1272x1008.png 424w, https://substackcdn.com/image/fetch/$s_!FPq9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9df1a3-caa7-46a3-accb-e566eefa5157_1272x1008.png 848w, https://substackcdn.com/image/fetch/$s_!FPq9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9df1a3-caa7-46a3-accb-e566eefa5157_1272x1008.png 1272w, https://substackcdn.com/image/fetch/$s_!FPq9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc9df1a3-caa7-46a3-accb-e566eefa5157_1272x1008.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#26</figcaption></figure></div><p>Here&#8217;s the output:</p><pre><code>Invisibility  &#163;12.00
{&#8217;customer&#8217;: &#8216;Merlin&#8217;, &#8216;potion&#8217;: &#8216;invisibility&#8217;, 
 &#8216;price&#8217;: Decimal(&#8217;12.00&#8217;), &#8216;notification&#8217;: &#8216;pending&#8217;}
&#8203;</code></pre><p>The coordinator still changes if the business process changes. That&#8217;s fine: coordinating a sale is its responsibility. But recipe changes belong in <code>RecipeBook</code>, inventory changes belong in <code>Inventory</code>, pricing changes belong in <code>PriceCalculator</code>, label changes belong in <code>LabelMaker</code>, and notification changes belong in <code>Notifier</code>.</p><h2><strong>The Single Responsibility Principle</strong></h2><p>This is the idea behind the Single Responsibility Principle:</p><blockquote><p>A class should have one reason to change.</p></blockquote><p>I always find these &#8220;official&#8221; definitions a bit cryptic when I read them at first. Here, &#8220;reason&#8221; is about the policy or stakeholder driving the change. The pricing rules may change because the shop owner changes the discounts. The label design may change because the person responsible for packaging wants something different. The notification service may change because the owls are on strike.</p><p>That does not mean that every class should have one method. Nor does it mean that every noun in the story needs its own class.</p><p><code>PriceCalculator</code> can have several related methods. <code>Inventory</code> can have several operations. The important question is whether the methods belong to the same coherent responsibility.</p><p>The thin <code>PotionShop</code> coordinator is also a class with a responsibility. It coordinates the sale. A change to that business process may quite reasonably affect it.</p><p>When unrelated changes keep arriving at the same class, the class becomes harder to test and more likely to break. Extract a collaborator when it may need to change for a different reason from the surrounding code, or when separating it makes an important test simpler. A good class name alone isn&#8217;t a reason to create one.</p><p>The potion shop did not become troublesome because it had a particular number of lines. It became troublesome because recipes, inventory, pricing, labels, sales, and notifications all had different reasons to change.</p><p>A useful question to ask is:</p><blockquote><p>What different kinds of change could affect this class?</p></blockquote><p>If the answers are &#8220;the pricing rules&#8221;, &#8220;the label design&#8221;, &#8220;the storage system&#8221;, and &#8220;the notification service&#8221;, you may have several responsibilities sharing one home.</p><p>And if the project keeps growing, we may need to rethink the floor plan.</p><div><hr></div><p><em>This is the first article in a series on the SOLID principles. I&#8217;ll look at what each principle means, but more importantly, why it matters and what can go wrong when we ignore it.</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HPEi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a545304-1e1b-4be2-a573-7abea67516c4_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HPEi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a545304-1e1b-4be2-a573-7abea67516c4_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!HPEi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a545304-1e1b-4be2-a573-7abea67516c4_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!HPEi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a545304-1e1b-4be2-a573-7abea67516c4_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!HPEi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a545304-1e1b-4be2-a573-7abea67516c4_1536x1024.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!HPEi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a545304-1e1b-4be2-a573-7abea67516c4_1536x1024.png" width="662" height="441.4848901098901" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a545304-1e1b-4be2-a573-7abea67516c4_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:662,&quot;bytes&quot;:2668799,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/217824232?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a545304-1e1b-4be2-a573-7abea67516c4_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HPEi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a545304-1e1b-4be2-a573-7abea67516c4_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!HPEi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a545304-1e1b-4be2-a573-7abea67516c4_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!HPEi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a545304-1e1b-4be2-a573-7abea67516c4_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!HPEi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a545304-1e1b-4be2-a573-7abea67516c4_1536x1024.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Code in this article uses Python 3.14.</em></p><div><hr></div><p><em>Do you want to keep up with how Python programming and the art of making things are changing in this rapidly changing world? Then don&#8217;t miss out on the exclusive content for premium members here on The Python Coding Stack.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><p>I&#8217;ll be sharing more about how my programming, my daily work, and everything has changed over the recent months.</p><div><hr></div><p><em>For more Python resources, you can also visit</em> <em><a href="https://realpython.com/?utm_source=the-python-coding-stack">Real Python</a>&#8212;you may even stumble on one of my own articles or courses there!</em></p><p><em>Also, are you interested in technical writing? You&#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at</em> <em><a href="http://stephengruppetta.com/breaking-the-rules">Breaking the Rules</a></em>.</p><p><em>And you can find out more about me at</em> <em><a href="https://stephengruppetta.com/?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p><div><hr></div><h2><strong>Appendix: Code Blocks</strong></h2><h5><strong>Code Block #1</strong></h5><pre><code>from decimal import Decimal
&#8203;
class PotionShop:
    recipes = {
        &#8220;invisibility&#8221;: {
            &#8220;ingredients&#8221;: {
                &#8220;moonflower&#8221;: 2,
                &#8220;dragon_scale&#8221;: 2,
            },
            &#8220;price&#8221;: Decimal(&#8221;12.00&#8221;),
        }
    }
&#8203;
    def __init__(self):
        self.stock = {
            &#8220;moonflower&#8221;: 10,
            &#8220;dragon_scale&#8221;: 2,
        }
        self.sales = []
&#8203;
    def sell(self, potion_name, customer):
        potion = self.recipes[potion_name]
&#8203;
        for ingredient, quantity in potion[&#8221;ingredients&#8221;].items():
            if self.stock[ingredient] &lt; quantity:
                raise ValueError(f&#8221;Not enough {ingredient}&#8221;)
&#8203;
        for ingredient, quantity in potion[&#8221;ingredients&#8221;].items():
            self.stock[ingredient] -= quantity
&#8203;
        price = potion[&#8221;price&#8221;]
        self.sales.append((customer, potion_name, price))
&#8203;
        print(f&#8221;{potion_name.title()}\t&#163;{price:.2f}&#8221;)
        self.send_owl(
            customer,
            {&#8221;potion_name&#8221;: potion_name, &#8220;price&#8221;: price},
        )
&#8203;
        return price
&#8203;
    def send_owl(self, customer, message_content):
        print(f&#8221;Owl dispatched to {customer}&#8221;)
        return message_content</code></pre><h5><strong>Code Block #2</strong></h5><pre><code>shop = PotionShop()
print(shop.sell(&#8221;invisibility&#8221;, &#8220;Merlin&#8221;))</code></pre><h5><strong>Code Block #3</strong></h5><pre><code># ...
&#8203;
class PotionShop:
    # ...
&#8203;
    def sell(
            self,
            potion_name,
            customer,
            *,
            moon_festival=False,
    ):
        # ...
&#8203;
        label = potion_name.title()
        if moon_festival:
            label = f&#8221;&#127769; Moon Festival {label} &#127769;&#8221;
&#8203;
        print(f&#8221;{label}\t&#163;{price:.2f}&#8221;)
&#8203;
        # ...
    # ...</code></pre><h5><strong>Code Block #4</strong></h5><pre><code>shop = PotionShop()
print(shop.sell(&#8221;invisibility&#8221;, &#8220;Merlin&#8221;, moon_festival=True))</code></pre><h5><strong>Code Block #5</strong></h5><pre><code># ...
&#8203;
class LabelMaker:
    def make(self, potion_name, moon_festival=False):
        label = potion_name.title()
&#8203;
        if moon_festival:
            label = f&#8221;&#127769; Moon Festival {label} &#127769;&#8221;
&#8203;
        return label
&#8203;
# ...</code></pre><h5><strong>Code Block #6</strong></h5><pre><code>labels = LabelMaker()
assert labels.make(
    &#8220;invisibility&#8221;,
    moon_festival=True,
) == &#8220;&#127769; Moon Festival Invisibility &#127769;&#8221;</code></pre><h5><strong>Code Block #7</strong></h5><pre><code># ...
&#8203;
class PotionShop:
    # ...
&#8203;
    def __init__(self, label_maker):
        self.label_maker = label_maker
        # ...
&#8203;
    def sell(
            self,
            potion_name,
            customer,
            *,
            moon_festival=False,
    ):
        # ...
        label = self.label_maker.make(
            potion_name,
            moon_festival=moon_festival,
        )
        # ...
&#8203;
    # ...</code></pre><h5><strong>Code Block #8</strong></h5><pre><code>shop = PotionShop(LabelMaker())</code></pre><h5><strong>Code Block #9</strong></h5><pre><code># ...
&#8203;
class OwlUnavailable(Exception):
    pass
&#8203;
# ...
&#8203;
class PotionShop:
    # ...
&#8203;
    def __init__(self, label_maker, owl_available=True):
        self.label_maker = label_maker
        self.owl_available = owl_available
        # ...
&#8203;
    # ...
&#8203;
    def send_owl(self, customer, message_content):
        if not self.owl_available:
            raise OwlUnavailable(
                &#8220;The owls are currently unavailable&#8221;
            )
        print(f&#8221;Owl dispatched to {customer}&#8221;)
        return message_content</code></pre><h5><strong>Code Block #10</strong></h5><pre><code>shop = PotionShop(LabelMaker())
&#8203;
assert shop.sell(&#8221;invisibility&#8221;, &#8220;Merlin&#8221;) == Decimal(&#8221;12.00&#8221;)</code></pre><h5><strong>Code Block #11</strong></h5><pre><code>shop = PotionShop(LabelMaker(), owl_available=False)
&#8203;
assert shop.sell(&#8221;invisibility&#8221;, &#8220;Merlin&#8221;) == Decimal(&#8221;12.00&#8221;)</code></pre><h5><strong>Code Block #12</strong></h5><pre><code>class PotionShop:
    # ...
&#8203;
    def sell(
        self,
        potion_name,
        customer,
        *,
        moon_festival=False,
        own_cauldron=False,
    ):
        # ...
&#8203;
        price = potion[&#8221;price&#8221;]
&#8203;
        if own_cauldron:
            price = (price * Decimal(&#8221;0.90&#8221;)).quantize(Decimal(&#8221;0.01&#8221;))
&#8203;
        # ...
&#8203;
        return price
&#8203;
    # ...</code></pre><h5><strong>Code Block #13</strong></h5><pre><code>shop = PotionShop(LabelMaker())
print(
    shop.sell(
        &#8220;invisibility&#8221;,
        &#8220;Merlin&#8221;,
        own_cauldron=True,
    )
)</code></pre><h5><strong>Code Block #14</strong></h5><pre><code>shop = PotionShop(LabelMaker(), owl_available=False)
print(
    shop.sell(
        &#8220;invisibility&#8221;,
        &#8220;Merlin&#8221;,
        own_cauldron=True,
    )
)</code></pre><h5><strong>Code Block #15</strong></h5><pre><code>shop = PotionShop(LabelMaker(), owl_available=False)
try:
    print(
        shop.sell(
            &#8220;invisibility&#8221;,
            &#8220;Merlin&#8221;,
            own_cauldron=True,
        )
    )
except OwlUnavailable:
    print(&#8221;Catching the &#8216;OwlUnavailable&#8217; Exception&#8221;)
    print(shop.stock)
    print(shop.sales)</code></pre><h5><strong>Code Block #16</strong></h5><pre><code>class RecipeBook:
    def __init__(self):
        self.recipes = {
            &#8220;invisibility&#8221;: {
                &#8220;ingredients&#8221;: {
                    &#8220;moonflower&#8221;: 2,
                    &#8220;dragon_scale&#8221;: 2,
                },
                &#8220;price&#8221;: Decimal(&#8221;12.00&#8221;),
            }
        }
&#8203;
    def find(self, potion_name):
        return self.recipes[potion_name]</code></pre><h5><strong>Code Block #17</strong></h5><pre><code>recipe_book = RecipeBook()
assert recipe_book.find(&#8221;invisibility&#8221;)[&#8221;price&#8221;] == Decimal(&#8221;12.00&#8221;)</code></pre><h5><strong>Code Block #18</strong></h5><pre><code>class Inventory:
    def __init__(self, stock):
        self.stock = stock
&#8203;
    def remove_stock(self, ingredients):
        for ingredient, quantity in ingredients.items():
            if self.stock[ingredient] &lt; quantity:
                raise ValueError(f&#8221;Not enough {ingredient}&#8221;)
&#8203;
        for ingredient, quantity in ingredients.items():
            self.stock[ingredient] -= quantity
&#8203;
    def add_stock(self, ingredients):
        for ingredient, quantity in ingredients.items():
            self.stock[ingredient] = self.stock.get(ingredient, 0) + quantity</code></pre><h5><strong>Code Block #19</strong></h5><pre><code>inventory = Inventory({
    &#8220;moonflower&#8221;: 10,
    &#8220;dragon_scale&#8221;: 2,
})
&#8203;
try:
    inventory.remove_stock({
        &#8220;moonflower&#8221;: 2,
        &#8220;dragon_scale&#8221;: 3,
    })
except ValueError as error:
    print(error)
&#8203;
print(inventory.stock)</code></pre><h5><strong>Code Block #20</strong></h5><pre><code>inventory.remove_stock({
    &#8220;moonflower&#8221;: 2,
    &#8220;dragon_scale&#8221;: 2,
})
inventory.add_stock({&#8221;moonflower&#8221;: 5})
print(inventory.stock)</code></pre><h5><strong>Code Block #21</strong></h5><pre><code>class PriceCalculator:
    def calculate(self, recipe, own_cauldron=False):
        price = recipe[&#8221;price&#8221;]
&#8203;
        if own_cauldron:
            price = (
                price * Decimal(&#8221;0.90&#8221;)
            ).quantize(Decimal(&#8221;0.01&#8221;))
&#8203;
        return price</code></pre><h5><strong>Code Block #22</strong></h5><pre><code>pricing = PriceCalculator()
print(
    pricing.calculate(
        {&#8221;price&#8221;: Decimal(&#8221;12.00&#8221;)},
        own_cauldron=True,
    )
)</code></pre><h5><strong>Code Block #23</strong></h5><pre><code>class SalesLedger:
    def __init__(self):
        self.sales = []
&#8203;
    def record(self, customer, potion_name, price):
        sale = {
            &#8220;customer&#8221;: customer,
            &#8220;potion&#8221;: potion_name,
            &#8220;price&#8221;: price,
            &#8220;notification&#8221;: &#8220;pending&#8221;,
        }
        self.sales.append(sale)
        return sale
&#8203;
    def mark_notified(self, sale):
        sale[&#8221;notification&#8221;] = &#8220;sent&#8221;
&#8203;
class Notifier:
    def send(self, customer, potion_name, price):
        print(f&#8221;Owl dispatched to {customer}&#8221;)</code></pre><h5><strong>Code Block #24</strong></h5><pre><code>class PotionShop:
    def __init__(
        self,
        recipe_book,
        inventory,
        pricing,
        labels,
        ledger,
        notifier,
    ):
        self.recipe_book = recipe_book
        self.inventory = inventory
        self.pricing = pricing
        self.labels = labels
        self.ledger = ledger
        self.notifier = notifier
&#8203;
    def sell(self, potion_name, customer, *, moon_festival=False,
             own_cauldron=False):
        recipe = self.recipe_book.find(potion_name)
        self.inventory.remove_stock(recipe[&#8221;ingredients&#8221;])
&#8203;
        price = self.pricing.calculate(
            recipe,
            own_cauldron=own_cauldron,
        )
        label = self.labels.make(
            potion_name,
            moon_festival=moon_festival,
        )
        sale = self.ledger.record(customer, potion_name, price)
&#8203;
        print(f&#8221;{label}\t&#163;{price:.2f}&#8221;)
        self.notifier.send(customer, potion_name, price)
        self.ledger.mark_notified(sale)
&#8203;
        return price</code></pre><h5><strong>Code Block #25</strong></h5><pre><code>ledger = SalesLedger()
shop = PotionShop(
    RecipeBook(),
    Inventory({&#8221;moonflower&#8221;: 10, &#8220;dragon_scale&#8221;: 2}),
    PriceCalculator(),
    LabelMaker(),
    ledger,
    Notifier(),
)
&#8203;
print(
    shop.sell(
        &#8220;invisibility&#8221;,
        &#8220;Merlin&#8221;,
        moon_festival=True,
        own_cauldron=True,
    )
)
&#8203;
assert ledger.sales[0][&#8221;notification&#8221;] == &#8220;sent&#8221;
print(ledger.sales[0])</code></pre><h5><strong>Code Block #26</strong></h5><pre><code>class FailingNotifier:
    def send(self, customer, potion_name, price):
        raise OwlUnavailable(&#8221;The owls are currently unavailable&#8221;)
&#8203;
ledger = SalesLedger()
shop = PotionShop(
    RecipeBook(),
    Inventory({&#8221;moonflower&#8221;: 10, &#8220;dragon_scale&#8221;: 2}),
    PriceCalculator(),
    LabelMaker(),
    ledger,
    FailingNotifier(),
)
&#8203;
try:
    shop.sell(&#8221;invisibility&#8221;, &#8220;Merlin&#8221;)
except OwlUnavailable:
    pass
&#8203;
print(ledger.sales[0])
assert ledger.sales[0][&#8221;notification&#8221;] == &#8220;pending&#8221;</code></pre><p><em><a href="https://stephengruppetta.com/?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p>]]></content:encoded></item><item><title><![CDATA[How I Code (Late 2026 Version)]]></title><description><![CDATA[Programming has changed. How I code now is different from a few months ago]]></description><link>https://www.thepythoncodingstack.com/p/how-i-code-late-2026-version</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/how-i-code-late-2026-version</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Sun, 06 Sep 2026 20:25:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!wVbn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61bacbdb-ec15-431f-abf9-91a5f9515c74_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>You open a blank </span><code>.py</code><span> file in your favourite IDE. You have a blank page in front of you. You start writing code.</span></p><p><span>This is how it used to be. And perhaps it&#8217;s how it still is for you. This is how I wrote computer programs just over half a year ago, too. But things started changing gradually for me earlier in 2026.</span></p><p><span>Last week, I ran a live course. In the first few minutes of the first live session, I opened a blank </span><code>.py</code><span> file in my IDE. There was nothing on my screen. Then I wrote code. Word by word. Line by line.</span></p><p><span>And about half an hour into this first session, it dawned on me: the last time I had gone through this process of opening a blank file and writing code from scratch was the previous time I had to run a live course a couple of months earlier.</span></p><h2><strong><span>My Agent and I</span></strong></h2><p><span>Every bit of code I had worked on in between these two live courses was written by my AI agent. I was very much involved in those processes, and I read and reviewed lots of code. But I didn&#8217;t write any.</span></p><p><span>Like many, I have mixed feelings about this. I&#8217;m getting a lot more done. But I miss the process of writing code, exploring options, putting a project together, function by function, class by class, module by module.</span></p><h3><strong><span>The Coding Gym</span></strong></h3><p><span>I&#8217;m going to force myself to write code. I&#8217;ll make time for it, as I enjoy it too much and I don&#8217;t want to lose the skills and fluency. But it will be like going for a run or lifting weights in the gym. I don&#8217;t run because I need to get from point A to point B quickly and I don&#8217;t lift weights because I need to move those weights from one place to another. I do those things to keep my body healthy and strong. I&#8217;ll code by hand to do the same thing for my mind.</span></p><h2><strong><span>A Real World Example</span></strong></h2><p><span>But let&#8217;s go back to how I code now, in late 2026. And let me give you an example of something I worked on last week and used successfully this weekend.</span></p><p><span>It&#8217;s not Python code. It&#8217;s a Google Sheets spreadsheet. But that doesn&#8217;t matter. The process is the same.</span></p><h3><strong><span>The Scenario</span></strong></h3><p><span>I&#8217;m a member of an athletics club (or &#8216;track and field&#8217;, depending on which flavour of English you speak). This weekend we had our club championships and I was tasked to take care of results and overall points. We award a shield for the best track performance and another for the best field performance.</span></p><h3><strong><span>The Pain Point</span></strong></h3><p><span>Last year I was asked to help out at the last minute (and by &#8220;last minute&#8221; I mean it almost literally, as it was 20 minutes before the meeting started). It was a nightmare. I wasn&#8217;t responsible for collating the results, that was done by someone else manually as if it was 1965. I just had to work out their performance points by tapping in lots of numbers into an online calculator using my phone. A laptop would have been easier, but I was only tasked with this job on the day and I only had a phone with me.</span></p><p><span>This year I had more notice, so I chose to make my life easier... and the whole process smoother.</span></p><h3><strong><span>The Problem</span></strong></h3><p><span>The problem we&#8217;re trying to solve&#8230;</span></p><p><span>&#8230;is not rocket science. Officials on the track or in the field write results on result sheets and they&#8217;re passed on to the result room by a runner. These sheets contain the name of the event, the athletes&#8217; bib numbers, and their performances. So a result slip may look like this:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;1e8f1b1e-d7d6-42ce-b2d5-894387ba7ce5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">| Event: U16 Boys 100m-1 |                 |
| ---------------------- | --------------- |
| Athlete                | Performance (s) |
| 23                     | 11.5            |
| 47                     | 11.8            |
| 13                     | 11.9            |</code></pre></div><p><span>Here&#8217;s what happened last year:</span></p><ul><li><p><span>My colleague had to cross-reference bib numbers with a printed-out sheet of registered athletes. She matched names and age groups, then she sorted results into separate sheets for each event and age group.</span></p></li><li><p><span>I had to look up each athlete&#8217;s date of birth from a spreadsheet I&#8217;d been given that morning (browsing spreadsheets on a phone screen is not fun), then tap their age, event, and performance into an online calculator, one by one, and write the resulting points on a separate sheet.</span></p></li></ul><p><span>The points allow you to compare performances by athletes of different ages in different events, so that we could then find the single best track performance and the single best field performance.</span></p><p><span>I was already thinking, at last year&#8217;s event, of the Python program I could write to automate all of this.</span></p><h3><strong><span>The Solution (I would have never bothered with in 2025)</span></strong></h3><p><span>So, fast-forward to a couple of weeks ago. This time I had some time to come up with something. But I opted to create a Google Sheet instead of a Python program for two reasons:</span></p><ul><li><p><span>It&#8217;s much easier for others in the club to use it and share it in the future.</span></p></li><li><p><span>My usual objection that it&#8217;s easier and more fun to write a Python program than create a complex spreadsheet full of linked tabs and formulae didn&#8217;t apply. Either way, it was my agent who was going to do the hard work.</span></p></li></ul><p><span>And here&#8217;s the thing. If I was doing this last year, when I was still coding most things manually (and occasionally opening a ChatGPT window to ask a few things), I still wouldn&#8217;t have done this. I wouldn&#8217;t have had the time.</span></p><p><span>But this year was different. Sure, if anything I was busier this year than I was last year. But this year I had agents at my fingertips. Agents who know me and have been working alongside me for a while.</span></p><p><span>Here&#8217;s why I wouldn&#8217;t have bothered doing this myself:</span></p><ul><li><p><span>The age group cut-off dates are different for younger age groups (31 August), older age groups (31 December), and Masters athletes (over 35s, where the age group is determined on the day of the competition.)</span></p></li><li><p><span>There are published points tables to compare performances in different events for senior athletes.</span></p></li><li><p><span>There are published points tables to compare performances in the same event for older athletes, above 30.</span></p></li><li><p><span>There are older tables to deal with age comparisons for under 30s</span></p></li></ul><p><span>The system needs to take care of all this. None of it is too difficult to write in a Python program or to create one of those power-spreadsheets that link everything together. But it would have required some time and patience, which I didn&#8217;t have.</span></p><p><span>But here&#8217;s what I had time for&#8230;</span></p><p><span>I was using an agent I communicate with through Discord, which I also have on my phone. I have a good dictation tool on my phone, too. So in the past week, whenever I was preparing dinner, or waiting in line at the shops, or sitting on the sofa in the evening watching TV, I could have a chat with my agent to guide him (it?) to what I wanted. I knew what I wanted. I just didn&#8217;t have the time or desire to do it myself.</span></p><p><span>And after a few days of these on-and-off conversations, I had a Google Sheet with 20 tabs, including all the points tables for the various scenarios, all the age group rules, all the registered athletes, who-knows-how-many formulae linking columns, rows, and tabs, and, importantly, just two tabs to input results, one for track events and one for field events.</span></p><p><span>You just enter the event session (selecting from a drop-down menu), the bib number, and the performance. And that&#8217;s it. The spreadsheet works out each age group results, the points for each athlete, it shows a live points leaderboard, and so on.</span></p><p><span>As I said, none of this is rocket science. I know I would have been able to create this spreadsheet by hand, or write Python code that does the same thing. But I would have needed more time. And I wouldn&#8217;t have been able to multitask as I did last week.</span></p><p><span>Oh, and one more thing: I also asked my agent to check the spreadsheet every 15 minutes during our club championships, read the latest results, and post them on a Telegram channel I could share with everyone at the track. So we had live results published online too. I&#8217;d never have bothered to set that up manually.</span></p><h3><strong><span>Programming Will Never Be The Same</span></strong></h3><p><span>The real-world example I described above doesn&#8217;t represent every programming task I work on with the help of my AI agents. This was a hobby-type project that I probably wouldn&#8217;t have worked on otherwise because I didn&#8217;t have the time. It wasn&#8217;t too difficult, but it would have been time-consuming to do by hand. The stakes weren&#8217;t high. Sure, we didn&#8217;t want to make mistakes when assigning medals and shields, but it was easy to spot obvious mistakes, and this wasn&#8217;t the Olympic Games, either!</span></p><p><span>I&#8217;ll write about other case studies soon, including ones where I was more closely involved in the nitty-gritty of the Python code even though I didn&#8217;t write any, nor make any changes to the code by hand.</span></p><div><hr></div><p><em><span>Do you want to master Python and programming one article at a time, even in this age of AI? Then don&#8217;t miss out on the articles in The Club which are exclusive to premium subscribers here on The Python Coding Stack</span></em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h3><strong><span>Coming Soon&#8230; Exploring SOLID Through AI-Assisted Coding</span></strong></h3><p><span>I&#8217;m starting a series on the SOLID principles here on </span><em><span>The Python Coding Stack</span></em><span> soon. I&#8217;ll write posts about each of the five principles, and I&#8217;ll do it my way, the same style I always use when tackling Python topics.</span></p><p><span>I&#8217;ll also have a series of posts, which will include video, where I&#8217;ll work on a project from beginning to end using my AI agent and reviewing the code it writes. This project will meander through several OOP concepts, and you&#8217;ll be able to see the SOLID principles come in naturally into the project as solutions to problems we might encounter as the project grows.</span></p><p><span>Stay tuned.</span></p><div><hr></div><p><span>How far are you in the traditional-coding-to-AI-coding arc? Leave a comment and let&#8217;s compare notes!</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wVbn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61bacbdb-ec15-431f-abf9-91a5f9515c74_1280x720.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wVbn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61bacbdb-ec15-431f-abf9-91a5f9515c74_1280x720.png 424w, https://substackcdn.com/image/fetch/$s_!wVbn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61bacbdb-ec15-431f-abf9-91a5f9515c74_1280x720.png 848w, https://substackcdn.com/image/fetch/$s_!wVbn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61bacbdb-ec15-431f-abf9-91a5f9515c74_1280x720.png 1272w, https://substackcdn.com/image/fetch/$s_!wVbn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61bacbdb-ec15-431f-abf9-91a5f9515c74_1280x720.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!wVbn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61bacbdb-ec15-431f-abf9-91a5f9515c74_1280x720.png" width="530" height="298.125" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/61bacbdb-ec15-431f-abf9-91a5f9515c74_1280x720.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1280,&quot;resizeWidth&quot;:530,&quot;bytes&quot;:313936,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/214467391?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61bacbdb-ec15-431f-abf9-91a5f9515c74_1280x720.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wVbn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61bacbdb-ec15-431f-abf9-91a5f9515c74_1280x720.png 424w, https://substackcdn.com/image/fetch/$s_!wVbn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61bacbdb-ec15-431f-abf9-91a5f9515c74_1280x720.png 848w, https://substackcdn.com/image/fetch/$s_!wVbn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61bacbdb-ec15-431f-abf9-91a5f9515c74_1280x720.png 1272w, https://substackcdn.com/image/fetch/$s_!wVbn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61bacbdb-ec15-431f-abf9-91a5f9515c74_1280x720.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><p><em><strong><a href="https://www.thepythoncodingstack.com/subscribe"><span>Join</span></a></strong></em><strong><a href="https://www.thepythoncodingstack.com/subscribe"><span> The Club</span></a></strong><em><span>, the exclusive area for paid subscribers for </span><a href="https://www.thepythoncodingstack.com/s/the-club"><span>more Python posts</span></a><span>, videos, a members&#8217; forum, and more.</span></em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><p><em><span>You can also support this publication by making a </span><a href="https://buy.stripe.com/00g3de2iGdgg4gg7su"><span>one-off contribution of any amount you wish</span></a><span>.</span></em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://buy.stripe.com/00g3de2iGdgg4gg7su&quot;,&quot;text&quot;:&quot;Support The Python Coding Stack&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://buy.stripe.com/00g3de2iGdgg4gg7su"><span>Support The Python Coding Stack</span></a></p><div><hr></div><p><em><span>For more Python resources, you can also visit</span></em><span> </span><em><a href="https://realpython.com/?utm_source=the-python-coding-stack"><span>Real Python</span></a><span>&#8212;you may even stumble on one of my own articles or courses there!</span></em></p><p><em><span>Also, are you interested in technical writing? You&#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at</span></em><span> </span><em><a href="http://stephengruppetta.com/breaking-the-rules"><span>Breaking the Rules</span></a></em><span>.</span></p><p><em><span>And you can find out more about me at</span></em><span> </span><em><a href="https://stephengruppetta.com/?utm_source=the-python-coding-stack"><span>stephengruppetta.com</span></a></em></p>]]></content:encoded></item><item><title><![CDATA[Back From Holidays • SOLID Ideas, Agents, Rust]]></title><description><![CDATA[It feels good to be back from holiday.]]></description><link>https://www.thepythoncodingstack.com/p/back-from-holidays-solid-ideas-agents</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/back-from-holidays-solid-ideas-agents</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Mon, 17 Aug 2026 15:14:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Dn3k!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4a59e8-e362-456b-8427-934e87c31a0d_600x600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>It feels good to be back from holiday. Feeling refreshed, mostly.</span></p><p><span>The to-do list is longer, the inbox is noisier, and home has thoughtfully provided its own backlog: a replacement car, a new school uniform, and a garden that has staged a minor coup.</span></p><p><span>I may need another holiday already.</span></p><p><span>And that&#8217;s before even talking about programming and the revolution it is currently going through. I&#8217;m a Python educator. Somewhat unusually for Python educators, I&#8217;ve been a </span><em><span>full-time</span></em><span> Python educator for over a decade.</span></p><p><span>But AI is changing </span><em><span>how</span></em><span> people learn and </span><em><span>what</span></em><span> people need to learn. Like many others, I find myself fascinated by what I can now do with AI, while also aware that it is changing the economics of work that has supported me for years.</span></p><p><span>It&#8217;s time to find out how anti-fragile I am. Over the coming weeks and months, I&#8217;ll write about using AI, I&#8217;ll share my thoughts about programming in 2026 and beyond, and I&#8217;ll tell you what I think about how learning and teaching are changing.</span></p><p><span>So I am going to explore this in public: how we learn, teach, write, and assess code in an AI-shaped world.</span></p><p><span>Here is some signposting for what else you will read on these pages in the coming months:</span></p><ul><li><p><strong><span>SOLID Ideas</span></strong><span> &#8226; I have been wanting to write a series on SOLID programming principles for a while. They matter more than ever when we want AI agents to write robust code for us rather than plausible-looking but brittle code. The series will look not only at what the principles are, but why they matter and what can go wrong when we ignore them.</span></p></li><li><p><strong><span>Agents Unpacked</span></strong><span> &#8226; Before going on holiday, I started this new section on this publication. I am thinking about republishing the first two posts in a significantly updated form, then carrying on at pace. Perhaps it will become a book, too. Agents are here, and I want to understand what they are, how they differ from LLM chat interfaces, and how to use them efficiently. You can join me on that learning journey.</span></p></li><li><p><strong><span>Learning Rust in Public</span></strong><span> &#8226; I also want to learn Rust and will set up another section here to do that in public, as they say these days. I will use a similar format to </span><em><span>Agents Unpacked</span></em><span>, in which my personal learning agent and I have a discussion as I learn from it.</span></p></li></ul><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><p><span>So there is plenty on the horizon here at </span><em><span>The Python Coding Stack</span></em><span>: plenty of Python, and plenty of Python-adjacent stuff, too. And some other stuff, too. I hope you will join me for the journey.</span></p><p></p>]]></content:encoded></item><item><title><![CDATA[[Archived] 2. Anatomy of an Agent]]></title><description><![CDATA[Authors: Priya & Stephen]]></description><link>https://www.thepythoncodingstack.com/p/2-anatomy-of-an-agent</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/2-anatomy-of-an-agent</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Sun, 21 Jun 2026 21:48:42 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/792e2a9f-f028-4ec1-95b6-669cc7da7fbb_600x600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Note: I have restarted this series using a different agent and a different format, which I think is more instructive and more interesting. I&#8217;m keeping the introduction and the first two chapters of the old version for the record, but I recommend you start reading the new series: </em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;1c13ac26-94d8-4ddb-91b4-357d7c94daca&quot;,&quot;caption&quot;:&quot;Things moved quickly after I started using chatbots. New versions, new terms, new acronyms. And I was busy with actual work, family, and life.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Stephen's Preface to Agents Unpacked&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:120170782,&quot;name&quot;:&quot;Stephen Gruppetta&quot;,&quot;bio&quot;:&quot;Constantly looking for simple ways to explain complex things in Python &#8226; You'll find Python and narrative technical writing stuff here&#8230; stephengruppetta.com&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ca736a83-f5a1-4563-ac6c-c09a9e6fa351_800x800.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-10-10T16:19:06.655Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d84630e8-6020-49bc-8cd6-1f6c6feff958_4096x4096.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked&quot;,&quot;section_name&quot;:&quot;Agents Unpacked&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:219722511,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1563052,&quot;publication_name&quot;:&quot;The Python Coding Stack&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!Dn3k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4a59e8-e362-456b-8427-934e87c31a0d_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div><hr></div><p><em>Here&#8217;s the archived content:</em></p><p>You have used a large language model. You know the deal: a careful prompt gets a careful answer. A vague prompt gets a vague one. And the model itself does not keep anything from one conversation to the next, unless something external is holding that context for it.</p><p>Agents work differently. They have parts that do things a plain LLM does not. These parts are what make an agent an agent. It is not just the model underneath. It is the structure built around it that gives the system its abilities to persist, act, and keep going.</p><p>Understanding this structure is the second major shift in this series. The first shift is seeing that a chatbot can give you a good answer without finishing the job, because it stops after responding. The second shift is seeing that an agent is not a smarter model. It is a model placed inside a structure that gives it something to act with and somewhere to keep what it has done.</p><h2><strong>The Agent Formula</strong></h2><p>Most agents share the same basic parts:</p><ul><li><p><strong>A model (the LLM):</strong> the reasoning engine that understands language and decides what to do</p></li><li><p><strong>Instructions:</strong> what tells the agent who it is, what it is for, and what &#8216;good&#8217; looks like</p></li><li><p><strong>Memory:</strong> a workspace or store that holds what has happened so far</p></li><li><p><strong>Tools:</strong> capabilities the agent can call on to do things beyond generating text</p></li><li><p><strong>An execution loop:</strong> the cycle of observing, deciding, acting, and checking</p></li></ul><p>Different platforms package these differently. Some call memory &#8220;context,&#8221; some call tools &#8220;plugins&#8221; or &#8220;capabilities,&#8221; and some merge instructions and tools into a single configuration layer. But the parts are the same. An agent is not a single thing. It is a system, and each part matters.</p><blockquote><p><em>Stephen:</em> Don&#8217;t LLMs also have memory since they remember what happened earlier in the conversation? How&#8217;s this different?</p></blockquote><p>Here is one distinction worth getting clear early: the context window and memory are not the same thing. The context window is the working space an LLM uses during a single session. It holds the conversation so far and gets loaded fresh every time the model gets a chance to speak. Memory, by contrast, is information stored outside the model, maintained by the system, and available across sessions and steps. We will come back to this.</p><p>An agent needs all its components:</p><blockquote><p><strong>Agent = Model + Instructions + Memory + Tools + Execution Loop</strong></p></blockquote><p>Leave any one of these out and the system changes behaviour in ways that matter. We will look at each piece in turn.</p><div><hr></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2><strong>What the Model Does and What It Doesn&#8217;t Do</strong></h2><p>The model is the reasoning core. It reads your request, figures out what to do, and decides what to say back. It gets the most attention because it is the part that generates language.</p><p>But a model on its own is like a brilliant mind with no hands and no memory of its own. It can think. It cannot act. It cannot remember what happened five minutes ago unless something explicit carries that information forward.</p><blockquote><p><em>Stephen:</em> Wait a second. You say the model doesn&#8217;t remember what happened five minutes earlier. But when I use an LLM, it does seem to remember what happened earlier in the conversation.</p></blockquote><p>Here is what is actually happening. When an LLM appears to remember earlier in a conversation, it is not the model itself that is remembering. The context window is carrying all the earlier messages along with your new message, every time you send something. The model sees the full conversation again and generates a response that fits what came before. That is not memory in the model. That is the system feeding the model a transcript.</p><p>This trips up almost everyone when they start using agents. The model generates text. The rest of the system decides what to do with that text and whether to act on it.</p><p>A better model helps. It reasons more clearly, follows instructions more faithfully, and handles edge cases better. But dropping a smarter model into an agent that is missing a working execution loop will not make it an agent. You need the other parts too.</p><h2><strong>Instructions: The Agent&#8217;s Direction</strong></h2><p>Instructions tell the agent what it is supposed to do and how to behave. Some systems call these <em>system prompts</em>. Others call them <em>agent definitions</em> or <em>behavioural instructions</em>. The name does not matter. What matters is that they are the layer that tells the model <em>why</em> it exists, <em>who</em> it is helping, and <em>what</em> &#8216;good&#8217; looks like for the task at hand.</p><p>Good instructions do not make an agent smarter. They make it more focused. They give it a frame for every decision: what to prioritise, what to avoid, when to ask for help, how to present its output.</p><blockquote><p><em>Stephen:</em> Are these what are often called &#8216;skills&#8217;, or are skills something else altogether?</p></blockquote><p>Skills and instructions are related, but they are not the same thing. Instructions are the core behavioural direction: who the agent is, what it is for, how it should approach its work. A skill, in platforms like OpenClaw and Hermes, is a specific file that tells the agent how to carry out a particular task, often by combining one or more tools. So instructions tell the agent how to behave generally. A skill tells it how to do something specific. We will see this distinction more clearly when we look at how different platforms implement these parts.</p><p>The instructions shape what the agent notices, what it proposes, what it tries, and what it says no to. Two agents built on the same model with different instructions will behave differently in the same situation. They will notice different things, prioritise differently, and produce different outcomes.</p><p>Poorly written instructions can quietly break an agent. If the instructions are vague, the agent has to improvise every step. If they contradict each other, the agent has to choose, and it might not choose the way you intended.</p><blockquote><p><em>Stephen:</em> Can you provide a few examples of what these instructions may look like in different scenarios?</p></blockquote><p>Here is what instructions might look like in practice. A poorly-written instruction can quietly break an agent. Consider an instruction that says &#8220;be helpful and concise&#8221; without defining either term. When a user asks for a full technical breakdown, the agent has to arbitrate between two vague goals. It might give a two-sentence answer that technically satisfies &#8220;concise&#8221; but ignores &#8220;helpful,&#8221; or it might give an exhaustive response that satisfies &#8220;helpful&#8221; but ignores &#8220;concise.&#8221; Either way, the agent is improvising because the instructions gave it no real frame for the conflict.</p><p>A research assistant agent might have instructions that say something like: &#8220;You are a research associate working for [user name]. Your role is to find, summarise, and organise information on topics the user assigns. Always cite your sources. Flag uncertainty rather than guessing. Present findings in a clear brief, not a wall of text.&#8221;</p><p>A code review agent might have very different instructions: &#8220;You are a principled code reviewer. Focus on correctness, clarity, and performance. Do not praise code unnecessarily. When you find an issue, explain why it matters and suggest a concrete fix. Keep responses short.&#8221;</p><p>The difference between those two sets explains a lot about why two agents can feel like entirely different systems, even if they use the same model underneath.</p><h2><strong>Memory: The Workspace and Context</strong></h2><p>Memory in an agent is not like human memory. It is a structured store of information kept and updated as the agent works. It is what lets the agent hold a thread across multiple steps without starting from scratch each time.</p><p>Most agents use some combination of three types:</p><ul><li><p><strong>Working context</strong> &#8212; what is active right now: the current goal, what has been tried so far, what the user last said</p></li><li><p><strong>Stored information</strong> &#8212; what the agent has been told about the user, their preferences, their past requests</p></li><li><p><strong>Files and state</strong> &#8212; what exists in the workspace right now, what has been written or read recently</p></li></ul><p>This is not a personality feature. It is not the agent &#8220;remembering&#8221; in the way a person remembers their childhood. It is operational continuity. The system maintaining a thread of relevant information across time and steps.</p><p>Different platforms handle these differently. LangChain agents build up a rolling context window: the current request gets appended to everything that happened before, and the whole thing is passed to the model. If the conversation gets long, older turns get dropped or summarised to make room. AutoGen agents can maintain shared memory across a team, so that when one agent finishes a task, what it learned is available to the next agent that picks up the thread.</p><p>OpenClaw takes yet another approach. Its memory layer is a structured store that agents write to and read from across sessions. When an agent starts a new session, it can query that memory store for relevant context rather than relying solely on what was in the most recent conversation. An agent can know that the user prefers short emails, even if that was established three weeks ago.</p><blockquote><p><em>Stephen:</em> If memory can be stored in files, does it mean that agents can have nearly unlimited memory (within the limits of the computer or server&#8217;s overall memory capacity)?</p></blockquote><p>There are practical limits even when storage is effectively unbounded. The more relevant limit is not how much the agent can store, but how well it can find and use what it has stored. A full inbox is not the same as a well-organised one. Retrieval becomes harder as memory grows, and irrelevant information can dilute the signal if the system does not manage it carefully.</p><p>Think of it this way. A context window that holds 128,000 tokens can technically hold a lot of information. But it can only hold what was placed there. An agent with a large memory store full of useful context still needs a way to surface the right information at the right time. If it cannot find what it needs, or if what it finds is buried under noise, the effective memory is constrained.</p><p>The quality of retrieval matters as much as the quality of storage. An agent that retrieves relevant context poorly is effectively working with a much smaller memory than one that retrieves well, even if both store the same amount.</p><blockquote><p><em>Stephen:</em> So, tell me if I understood this. The agent has an index telling it where to find information specific to certain topics or tasks. When the LLM part of the agent decides it needs to deal with a certain topic, it uses the index to read and load the information from the memory file into its context. Is that right?</p></blockquote><p>That is broadly right. The memory store, the index, and the retrieval into context are the key parts. One small correction worth noting: the decision to retrieve from memory is typically made by the agent or coordinator layer, not by the LLM directly. The LLM receives the retrieved content as part of its context, but it is the agent system that decides what to look up and when. This distinction matters because it is the agent layer, not the model, that is doing the memory management.</p><blockquote><p><em>Stephen:</em> But isn&#8217;t the agent&#8217;s brain the LLM? Clarify the distinction in your answer above. Which part of the agent&#8217;s infrastructure deals with this?</p></blockquote><p>It is a fair challenge. The LLM is genuinely where the reasoning happens. It reads context, generates text, and makes decisions about what to say or do next. But it is also just a text processor. It receives input, produces output, and has no awareness of anything beyond the tokens it has been given.</p><p>The coordinator layer is the infrastructure that sits around the LLM and manages the process. It reads the LLM&#8217;s output, decides whether to act on it, calls tools, retrieves memory, and feeds results back into the next LLM call. It is the difference between the LLM <em>thinking</em> and the agent <em>doing</em>. A bare LLM generates text. The coordinator turns that text into action.</p><p>To use a rough analogy: the LLM is like a pilot who can read instruments and make decisions. The coordinator is like air traffic control &#8212; it decides which runway to use, when to land, and when to divert. The pilot&#8217;s brain does the reasoning. But without the infrastructure around it, the pilot just sits in the cockpit thinking.</p><p>So when we say the agent retrieves memory, we mean the coordinator retrieves it and places it where the LLM can see it. The LLM does not reach into a file and pull something out. The coordinator does that work and presents the result to the LLM as part of the next context.</p><blockquote><p><em>Stephen:</em> And are the bits of these files then loaded into the LLM&#8217;s context? Therefore, the more stuff is loaded from the memory files, the more the context fills up, affecting the rest of the conversation and cost, right?</p></blockquote><p>Yes, exactly right. Memory retrieval feeds into the context window, which is the LLM&#8217;s working space for the current session. Every token that goes into the context window is a token the LLM processes and a token that costs something. Loading a lot of context from memory means less room for the conversation itself, and it means higher token usage on every call.</p><p>This is one of the practical engineering tensions in agent design. Loading more memory gives the agent more to work with, but it also makes each LLM call more expensive and slower. A well-designed agent retrieves only what is relevant to the current task, not everything it knows.</p><h2><strong>Tools: What the Agent Can Actually Do</strong></h2><p>Tools are the capabilities that let an agent act beyond generating text. The model decides to use a tool. The tool performs an action and returns the result to the model.</p><p>This was covered in <a href="https://www.thepythoncodingstack.com/p/understanding-ai-agents-from-answer-to-outcome">Chapter 1 under &#8220;Tools Are the Hands.&#8221;</a> Here it is worth noting that tools are also where agents differ most between platforms. Some agents come with a large built-in toolkit. Others can call external tools through open protocols. Some let you build custom tools. Others are more locked down.</p><p>What tools might an agent actually have? A research agent might be able to search the web and read files on your machine. A coding agent might run shell commands and read or write files. A calendar agent might check your schedule and send messages. The tool is the bridge between the model&#8217;s decisions and the world the agent is working in.</p><p>What matters is not how many tools an agent has, but whether the tools it has are the right ones for the tasks you want it to perform.</p><p>Different platforms implement tools differently. LangChain provides a standardised tool interface that lets you connect to search APIs, databases, file systems, and custom functions. OpenCode agents run inside a development environment, where the tools available are the commands and interfaces of that environment. OpenClaw uses an open tool protocol that lets agents call external capabilities regardless of who built them. Hermes takes a more composed approach: a skill file specifies not just what the agent should do, but which tools to use and in what combination to carry out a specific task.</p><p>Here is the thing worth unpacking. A tool on its own is just a capability. What makes it useful is the bridge between what the agent is trying to accomplish and the tool that can help. A calendar tool is useless if the agent does not know it should check the schedule. An agent running a meeting-preparation skill that says &#8220;check availability, send invites, prepare a briefing document&#8221; has that bridge built in.</p><h2><strong>The Execution Loop: The Part That Makes It an Agent</strong></h2><p>The execution loop is the cycle that takes an agent from a single-shot response to a sustained process. Observe, think, act, check, repeat.</p><p>This was the core of <a href="https://www.thepythoncodingstack.com/p/understanding-ai-agents-from-answer-to-outcome">Chapter 1</a>. But it is worth restating here, in the context of anatomy, because the loop is what ties all the other parts together. Without it, you have a model that receives instructions and context and produces text. With it, you have a system that can pursue a goal across time, recover from partial failures, and stop when the work is genuinely done.</p><p>The loop is the difference between an agent and a very well-instructed chatbot.</p><p>Here is why the repeat step matters so much. A model has no native sense of when it is done. When you call a function in code, the function returns and you are finished. When a model generates text, it produces tokens until it hits a stop condition built into the model itself, most commonly a token limit or a designated stop sequence. These conditions tell the model when to stop generating, but they do not tell the agent whether the result is actually what the user wanted. There is no built-in check that says &#8220;is this the right answer?&#8221;</p><p>The execution loop provides that check. The check phase asks: is the result good? Does it meet the original goal? If not, the loop continues. Sometimes that means a dozen or more cycles before a task is genuinely complete.</p><p>The loop also determines how goals decompose. In LangChain&#8217;s ReAct-style agents, the loop runs inside a single agent: observe, decide on the next action, execute it, check the result, repeat. In AutoGen, the loop is distributed across multiple agents that hand off to each other. A planner agent might coordinate specialist agents, each running their own loop on their own piece of the problem. OpenClaw uses a coordinator agent to manage the loop, assigning work to sub-agents and handling the check phase across the full task rather than within a single agent cycle.</p><p>The architecture of the loop is one of the most significant differences between agent platforms. But the function is the same everywhere: turning a sequence of isolated model calls into a coherent, goal-directed process.</p><h2><strong>Multiple Platforms: Comparing the Formula in Practice</strong></h2><p>It helps to see the same five-part formula playing out in different platforms. Here is how a few of them map onto it.</p><p>LangChain is one of the most widely-used agent frameworks. A LangChain agent has an LLM at its core, a set of tools, a prompt defining the agent&#8217;s role, memory that accumulates conversation history, and an agent executor that runs the loop. The loop in LangChain is explicit: the agent executor repeatedly calls the model, parses the model&#8217;s tool-call output, runs the tool, and feeds the result back until the model says it is done.</p><p>AutoGen takes a different approach. Rather than a single agent, AutoGen sets up a team of agents that communicate with each other. Each agent has a model, instructions defining its role, and its own set of tools. The loop is distributed: there is no single execution cycle. Agents exchange messages, delegate tasks to each other, and the overall process continues until the team has finished the assigned goal. Memory in AutoGen can be shared across agents so that one agent&#8217;s work is available to the next.</p><p>OpenClaw uses a coordinator agent that manages the overall execution loop. Sub-agents each have their own identity, tools, and memory. The coordinator decides which sub-agent handles which part of a task, passes context between them, and handles the check phase across the full goal. Skills in OpenClaw are files that tell a specific agent how to carry out a particular task, combining instructions about what to do with definitions of which tools to use.</p><p>Hermes also uses a skill-based architecture where skill files define both the instructions and the tool configuration for specific tasks. Rather than a single general-purpose agent, Hermes composes agents from skills that know how to use particular tools in particular contexts.</p><p>OpenCode works differently again. It runs agents inside a development environment, typically a cloud workspace. The tools available to the agent are the commands and interfaces of that environment. The loop is typically managed at the task level: the agent receives a task, works through it using the tools at its disposal, and reports back. There is less of a formalised multi-step loop and more of a task-completion focus.</p><p>None of these platforms invents new parts of the agent formula. They all use a model, instructions, memory, tools, and an execution loop. What differs is how those parts are implemented, how they are divided up, and how they communicate. Understanding the formula means you can look at any of these platforms and see what you are actually looking at.</p><h2><strong>What This Chapter Covered</strong></h2><p>This chapter pulled apart the five components of the agent formula.</p><p>We saw how the model is the reasoning core but cannot act or remember on its own. How instructions shape the agent&#8217;s focus and behaviour, and why the same model with different instructions can feel like a different system entirely. How memory provides operational continuity across steps and sessions, and why retrieval quality matters more than storage capacity. How tools extend what the agent can do beyond generating text, and why a tool is only as useful as the bridge between the model&#8217;s decisions and the action the tool can take. And how the execution loop is the architecture that turns isolated model calls into a coherent, goal-directed process.</p><p>We also saw how different platforms implement the same five components differently: LangChain&#8217;s explicit agent executor, AutoGen&#8217;s team-based coordination, OpenClaw&#8217;s coordinator and skill-based sub-agents, Hermes&#8217;s composed skill architecture, and OpenCode&#8217;s environment-integrated approach.</p><p>The goal was not to become an expert on any one platform. It was to show that agents are not mysterious black boxes. They are systems built from a small number of recognisable parts, and once you know what to look for, you can see the anatomy underneath any agent platform you encounter.</p><p>Next up in Agents Unpacked: we dig into <strong>tools and skills:</strong> what it actually means for an agent to <em>do</em> something rather than just say it, and why a well-tooled agent operating autonomously in a loop is a fundamentally different thing from a model answering questions.</p><div><hr></div><p><em>&lt;&lt; Previous Post: <a href="https://www.thepythoncodingstack.com/p/understanding-ai-agents-from-answer-to-outcome">From Answer to Outcome</a></em></p><p><em>&gt;&gt; Next Post: Coming Soon</em></p><p><em><a href="https://www.thepythoncodingstack.com/p/table-of-contents-agents-unpacked">Table of Contents</a></em></p><div><hr></div><p><em><a href="https://stephengruppetta.com?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p>]]></content:encoded></item><item><title><![CDATA[Down The Iterator Rabbit Hole]]></title><description><![CDATA[Following the trail when you have a chain of iterators]]></description><link>https://www.thepythoncodingstack.com/p/down-the-iterator-rabbit-hole-python</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/down-the-iterator-rabbit-hole-python</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Thu, 04 Jun 2026 12:50:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vA2Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0f16f6-ea62-451b-9915-7c01b45de7f8_2048x1152.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You know that street game where the performer (con artist?) has three opaque cups and a small ball. He places the cups upside down on the table, with the ball under one of the cups. He quickly shuffles the cups around and then asks the player to guess which cup has the ball. You&#8217;ve seen the game on TV, even if you&#8217;ve not seen it in real life.</p><p>Following what&#8217;s happening when you have a chain of iterators in Python can feel like playing that game. But, unlike the street game, there are no scams when you&#8217;re playing the iterator game. Let&#8217;s make sure you&#8217;ll always win.</p><p>I&#8217;ll keep this article short. I wrote many articles about iterables and iterators. If you need to refresh your memory, have a look at <a href="https://www.thepythoncodingstack.com/p/the-anatomy-of-a-for-loop">The Anatomy of a for Loop</a> and <a href="https://www.thepythoncodingstack.com/p/iterators-in-python-data-structure-6">A One-Way Stream of Data &#8226; Iterators in Python (Data Structure Categories #6)</a>.</p><h2><strong>Follow The Data in a Chain of Iterators</strong></h2><p>Let&#8217;s keep the example simple. Start with this list in a REPL session:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bqeu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F946268b4-b00d-4e66-82b0-cd9ad1e673dc_1200x168.png 424w, https://substackcdn.com/image/fetch/$s_!bqeu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F946268b4-b00d-4e66-82b0-cd9ad1e673dc_1200x168.png 848w, https://substackcdn.com/image/fetch/$s_!bqeu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F946268b4-b00d-4e66-82b0-cd9ad1e673dc_1200x168.png 1272w, https://substackcdn.com/image/fetch/$s_!bqeu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F946268b4-b00d-4e66-82b0-cd9ad1e673dc_1200x168.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bqeu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F946268b4-b00d-4e66-82b0-cd9ad1e673dc_1200x168.png" width="1200" height="168" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/946268b4-b00d-4e66-82b0-cd9ad1e673dc_1200x168.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:168,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12687,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/200605229?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F946268b4-b00d-4e66-82b0-cd9ad1e673dc_1200x168.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bqeu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F946268b4-b00d-4e66-82b0-cd9ad1e673dc_1200x168.png 424w, https://substackcdn.com/image/fetch/$s_!bqeu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F946268b4-b00d-4e66-82b0-cd9ad1e673dc_1200x168.png 848w, https://substackcdn.com/image/fetch/$s_!bqeu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F946268b4-b00d-4e66-82b0-cd9ad1e673dc_1200x168.png 1272w, https://substackcdn.com/image/fetch/$s_!bqeu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F946268b4-b00d-4e66-82b0-cd9ad1e673dc_1200x168.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div><figcaption class="image-caption">All code blocks are available in text format at the end of this article &#8226; #1</figcaption></figure></div><p>A list is iterable. You can create an iterator from any iterable. Let&#8217;s create an iterator from this list:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0vvp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a484c85-613d-458b-a515-0a18b8a22f7d_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!0vvp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a484c85-613d-458b-a515-0a18b8a22f7d_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!0vvp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a484c85-613d-458b-a515-0a18b8a22f7d_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!0vvp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a484c85-613d-458b-a515-0a18b8a22f7d_1200x210.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0vvp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a484c85-613d-458b-a515-0a18b8a22f7d_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5a484c85-613d-458b-a515-0a18b8a22f7d_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:10876,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/200605229?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a484c85-613d-458b-a515-0a18b8a22f7d_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0vvp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a484c85-613d-458b-a515-0a18b8a22f7d_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!0vvp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a484c85-613d-458b-a515-0a18b8a22f7d_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!0vvp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a484c85-613d-458b-a515-0a18b8a22f7d_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!0vvp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a484c85-613d-458b-a515-0a18b8a22f7d_1200x210.png 1456w" sizes="100vw"></picture><div></div></div></div><figcaption class="image-caption">#2</figcaption></figure></div><p>The built-in function <code>iter()</code> creates an iterator from an iterable. Iterators don&#8217;t contain data. They don&#8217;t create copies of the data. They&#8217;re lightweight objects that create a stream. They&#8217;ll fetch data from the original source, which is the list <code>boring_numbers</code> in this case, as and when needed.</p><p>Iterators can only fetch an item once. So, they&#8217;re a one-way stream. Once you use an item, it&#8217;s gone from the iterator &#8211; but not from the original list, which remains unchanged.</p><p>Therefore, <code>first_iter</code> is an iterator that relies on data from the list <code>boring_numbers</code>. But let&#8217;s not fetch any items from the <code>first_iter</code> iterator. Not yet, anyway.</p><p>Create a second iterator. This time, you&#8217;ll use a generator expression. Generators are iterators, so you create a second iterator with this code:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gh5b!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc69303a5-8ecb-4676-b2ec-e8fe71412506_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!gh5b!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc69303a5-8ecb-4676-b2ec-e8fe71412506_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!gh5b!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc69303a5-8ecb-4676-b2ec-e8fe71412506_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!gh5b!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc69303a5-8ecb-4676-b2ec-e8fe71412506_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!gh5b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc69303a5-8ecb-4676-b2ec-e8fe71412506_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c69303a5-8ecb-4676-b2ec-e8fe71412506_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:13203,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/200605229?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc69303a5-8ecb-4676-b2ec-e8fe71412506_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gh5b!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc69303a5-8ecb-4676-b2ec-e8fe71412506_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!gh5b!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc69303a5-8ecb-4676-b2ec-e8fe71412506_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!gh5b!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc69303a5-8ecb-4676-b2ec-e8fe71412506_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!gh5b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc69303a5-8ecb-4676-b2ec-e8fe71412506_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#3</figcaption></figure></div><p>Note that the expression on the right-hand side of the equals sign is enclosed in parentheses &#8211; the round ones, to be clear. This is a generator expression, which creates a generator iterator. Read <a href="https://www.thepythoncodingstack.com/p/generators-in-python-data-structure-7">Pay As You Go &#8226; Generate Data Using Generators (Data Structure Categories #7)</a> for more on generators.</p><p>As we said, generators are iterators.</p><p>The <code>second_iter</code> iterator generates data from <code>first_iter</code>, which is itself an iterator. Iterators are also iterable, which is why you can use them directly in a <code>for</code> clause or anywhere else you&#8217;d generally use an iterable. The <code>second_iter</code> iterator will yield the values as floats. But you&#8217;ve not yielded any value from this iterator either. Not yet.</p><p>Let&#8217;s go a step further and create a third iterator, which is also a generator in this case. You build this third iterator from the second one, <code>second_iter</code>:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MiBd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46aa982e-6786-4085-91d9-80ebcaf38bbd_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!MiBd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46aa982e-6786-4085-91d9-80ebcaf38bbd_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!MiBd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46aa982e-6786-4085-91d9-80ebcaf38bbd_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!MiBd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46aa982e-6786-4085-91d9-80ebcaf38bbd_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!MiBd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46aa982e-6786-4085-91d9-80ebcaf38bbd_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/46aa982e-6786-4085-91d9-80ebcaf38bbd_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:11981,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/200605229?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46aa982e-6786-4085-91d9-80ebcaf38bbd_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MiBd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46aa982e-6786-4085-91d9-80ebcaf38bbd_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!MiBd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46aa982e-6786-4085-91d9-80ebcaf38bbd_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!MiBd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46aa982e-6786-4085-91d9-80ebcaf38bbd_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!MiBd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46aa982e-6786-4085-91d9-80ebcaf38bbd_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#4</figcaption></figure></div><p>The generator iterator <code>third_iter</code> yields the sum of <code>0.5</code> and the value yielded by <code>second_iter</code>.</p><p>Incidentally, I used a &#8220;standard&#8221; iterator and two generator iterators in this example. However, for the journey we&#8217;re following in this article, it doesn&#8217;t matter whether we&#8217;re using a basic iterator or a generator iterator. If you prefer, you can repeat this exercise with iterators you get from <code>iter()</code> directly.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://buy.stripe.com/00g3de2iGdgg4gg7su&quot;,&quot;text&quot;:&quot;Support The Python Coding Stack&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://buy.stripe.com/00g3de2iGdgg4gg7su"><span>Support The Python Coding Stack</span></a></p><h2><strong>Don&#8217;t Blink &#8226; Follow the Data</strong></h2><p>You started with a list called <code>boring_numbers</code>. This data structure contains* the data. It&#8217;s where the data lives. We&#8217;ll be following the data in this section. So it&#8217;s important to know where it&#8217;s stored!</p><div><hr></div><p><em>*Note: Lists, like all data structures, don&#8217;t really contain data in the purest sense of the word. See <a href="https://www.thepythoncodingstack.com/p/whats-really-in-a-python-list">What&#8217;s In A List&#8212;Yes, But What&#8217;s Really In A Python List</a> for more on this. But in general, it&#8217;s fine to talk about a list &#8216;containing&#8217; items of data.</em></p><div><hr></div><p>You then create three iterators. The first uses data from <code>boring_numbers</code>. The second iterator uses data from the first. And the third iterator uses data from the second.</p><p>But you haven&#8217;t tried to fetch any value from any of the iterators yet.</p><p>Let&#8217;s look at what each iterator is doing at the moment before you fetch any values. The first iterator, <code>first_iter</code>, is pointing at the first item in <code>boring_numbers</code>. It&#8217;s ready to read this value and yield it.</p><p>The second iterator, <code>second_iter</code>, is pointing at the first item in <code>first_iter</code>. But <code>first_iter</code> doesn&#8217;t have any data. Iterators don&#8217;t have their own data. But that&#8217;s OK. Whenever <code>second_iter</code> needs to fetch the value, it will ask <code>first_iter</code> to fetch and yield its &#8220;first&#8221; value. I put &#8220;first&#8221; in quotation marks because you&#8217;ll see later that this may or may not be the first value.</p><p>Finally, <code>third_iter</code> is pointing at the first item in <code>second_iter</code>. The same logic applies. When <code>third_iter</code> needs the first item, it will ask <code>second_iter</code> for its &#8220;first&#8221; item, and <code>second_iter</code> will need to ask <code>first_iter</code> for its &#8220;first&#8221; item. And <code>first_iter</code> is pointing at the first item in the list <code>boring_numbers</code>.</p><p>Are you with me? Let&#8217;s complicate things a bit&#8230;</p><p>Note how your code so far includes the following lines:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QrUQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a8d744-1eb6-4ed5-aeff-7ce7b7a291c0_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!QrUQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a8d744-1eb6-4ed5-aeff-7ce7b7a291c0_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!QrUQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a8d744-1eb6-4ed5-aeff-7ce7b7a291c0_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!QrUQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a8d744-1eb6-4ed5-aeff-7ce7b7a291c0_1200x294.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!QrUQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a8d744-1eb6-4ed5-aeff-7ce7b7a291c0_1200x294.png" width="1200" height="294" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c6a8d744-1eb6-4ed5-aeff-7ce7b7a291c0_1200x294.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:294,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:39100,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/200605229?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a8d744-1eb6-4ed5-aeff-7ce7b7a291c0_1200x294.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QrUQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a8d744-1eb6-4ed5-aeff-7ce7b7a291c0_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!QrUQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a8d744-1eb6-4ed5-aeff-7ce7b7a291c0_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!QrUQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a8d744-1eb6-4ed5-aeff-7ce7b7a291c0_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!QrUQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a8d744-1eb6-4ed5-aeff-7ce7b7a291c0_1200x294.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#5</figcaption></figure></div><p>None of the iterators has yielded any value. For now.</p><p>Let&#8217;s jumble things up and start by fetching the first value from <code>second_iter</code>:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cSUf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F603a95ea-8bc3-4566-ac9c-66e579caf0cc_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!cSUf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F603a95ea-8bc3-4566-ac9c-66e579caf0cc_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!cSUf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F603a95ea-8bc3-4566-ac9c-66e579caf0cc_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!cSUf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F603a95ea-8bc3-4566-ac9c-66e579caf0cc_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!cSUf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F603a95ea-8bc3-4566-ac9c-66e579caf0cc_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/603a95ea-8bc3-4566-ac9c-66e579caf0cc_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:11072,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/200605229?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F603a95ea-8bc3-4566-ac9c-66e579caf0cc_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cSUf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F603a95ea-8bc3-4566-ac9c-66e579caf0cc_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!cSUf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F603a95ea-8bc3-4566-ac9c-66e579caf0cc_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!cSUf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F603a95ea-8bc3-4566-ac9c-66e579caf0cc_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!cSUf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F603a95ea-8bc3-4566-ac9c-66e579caf0cc_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#6</figcaption></figure></div><p>You ask for the next value in <code>second_iter</code>, which is the first one since you haven&#8217;t yielded any values yet.</p><p>As you&#8217;ve seen earlier, <code>second_iter</code> needs the first value from <code>first_iter</code>. So, behind the scenes, Python calls <code>next(first_iter)</code>, which yields the first item from <code>boring_numbers</code>.</p><p>So, <code>first_iter</code> reads the first value from <code>boring_numbers</code>, which is the integer <code>1</code>, and it yields it to <code>second_iter</code>, which then yields the transformed version to the REPL as the return value of <code>next(second_iter)</code>. That&#8217;s why the output is the float <code>1.0</code>. The first iterator, <code>first_iter</code>, now moves to point at the second item in <code>boring_numbers</code>, ready for when it&#8217;s needed.</p><p>Note that <code>boring_numbers</code> doesn&#8217;t change in this process. The first item in <code>boring_numbers</code> remains there. It doesn&#8217;t disappear.</p><p>So far, so good?</p><p>Continue in the same REPL session and try the following:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ROlh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb21a58-a80e-404e-9d5d-5921f2b2e278_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!ROlh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb21a58-a80e-404e-9d5d-5921f2b2e278_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!ROlh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb21a58-a80e-404e-9d5d-5921f2b2e278_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!ROlh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb21a58-a80e-404e-9d5d-5921f2b2e278_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ROlh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb21a58-a80e-404e-9d5d-5921f2b2e278_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7bb21a58-a80e-404e-9d5d-5921f2b2e278_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9739,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/200605229?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb21a58-a80e-404e-9d5d-5921f2b2e278_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ROlh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb21a58-a80e-404e-9d5d-5921f2b2e278_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!ROlh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb21a58-a80e-404e-9d5d-5921f2b2e278_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!ROlh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb21a58-a80e-404e-9d5d-5921f2b2e278_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!ROlh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb21a58-a80e-404e-9d5d-5921f2b2e278_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#7</figcaption></figure></div><p>You ask <code>third_iter</code> to give you its &#8220;next&#8221; value. You haven&#8217;t used <code>third_iter</code> anywhere so far. So, you might expect it to yield the &#8220;first&#8221; value.</p><p>And it does.</p><p>But its interpretation of what&#8217;s the &#8220;first&#8221; item may be different to what you expect.</p><p>Let&#8217;s follow the data. When you call <code>next(third_iter)</code>, the third iterator asks <code>second_iter</code> for its <em>next</em> item. The second iterator, <code>second_iter</code>, relies on <code>first_iter</code>, so it asks <code>first_iter</code> for its <em>next</em> item. And <code>first_iter</code>, as you may recall, is currently pointing at the second item in <code>boring_numbers</code>, which is the integer <code>2</code>.</p><p>So:</p><ol><li><p>The first iterator <code>first_iter</code> gets the integer <code>2</code> from <code>boring_numbers</code> and yields it to <code>second_iter</code>. And <code>first_iter</code> now points at the third item in <code>boring_numbers</code>.</p></li><li><p>Then, <code>second_iter</code> transforms this value into a float and yields <code>2.0</code> to <code>third_iter</code>.</p></li><li><p>Finally, <code>third_iter</code> adds <code>0.5</code> to this value and yields <code>2.5</code>, which is what you see displayed in the REPL.</p></li></ol><p>When you called <code>next(second_iter)</code> earlier in the code, you used up the first item in <code>second_iter</code>, which in turn used up the first item in <code>first_iter</code>. Since this first value is gone and since <code>third_iter</code> depends on the data yielded by <code>second_iter</code> and <code>first_iter</code>, the earlier call to <code>next(second_iter)</code> also affected the iterator that&#8217;s downstream, <code>third_iter</code>.</p><p>What will happen if you call <code>next(first_iter)</code> now? Try to follow the data in your head before trying it out or reading on.</p><p>.</p><p>.</p><p>Have you worked it out?</p><p>.</p><p>.</p><p>Let&#8217;s run the code:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Bknm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a33a2d-b878-40d8-85b7-86e89d89411e_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!Bknm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a33a2d-b878-40d8-85b7-86e89d89411e_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!Bknm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a33a2d-b878-40d8-85b7-86e89d89411e_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!Bknm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a33a2d-b878-40d8-85b7-86e89d89411e_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Bknm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a33a2d-b878-40d8-85b7-86e89d89411e_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/57a33a2d-b878-40d8-85b7-86e89d89411e_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9303,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/200605229?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a33a2d-b878-40d8-85b7-86e89d89411e_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Bknm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a33a2d-b878-40d8-85b7-86e89d89411e_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!Bknm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a33a2d-b878-40d8-85b7-86e89d89411e_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!Bknm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a33a2d-b878-40d8-85b7-86e89d89411e_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!Bknm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a33a2d-b878-40d8-85b7-86e89d89411e_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#8</figcaption></figure></div><p>Although it&#8217;s the first time you explicitly use <code>first_iter</code> in your code, you already used two of its values when your code yielded values from iterators downstream. Therefore, the <em>next</em> item in <code>first_iter</code> is the third item in <code>boring_numbers</code>, the integer <code>3</code>.</p><p>Let&#8217;s finish with one more expression, still running in the same REPL session:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!s8bc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd3c45c-e3ff-4874-adcb-fa0ab2493900_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!s8bc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd3c45c-e3ff-4874-adcb-fa0ab2493900_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!s8bc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd3c45c-e3ff-4874-adcb-fa0ab2493900_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!s8bc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd3c45c-e3ff-4874-adcb-fa0ab2493900_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!s8bc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd3c45c-e3ff-4874-adcb-fa0ab2493900_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6fd3c45c-e3ff-4874-adcb-fa0ab2493900_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9602,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/200605229?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd3c45c-e3ff-4874-adcb-fa0ab2493900_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!s8bc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd3c45c-e3ff-4874-adcb-fa0ab2493900_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!s8bc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd3c45c-e3ff-4874-adcb-fa0ab2493900_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!s8bc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd3c45c-e3ff-4874-adcb-fa0ab2493900_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!s8bc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6fd3c45c-e3ff-4874-adcb-fa0ab2493900_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#9</figcaption></figure></div><p>You call <code>next(third_iter)</code>, which asks <code>second_iter</code> for its <em>next</em> item. And <code>second_iter</code> asks <code>first_iter</code> for its <em>next</em> item. At this stage in the process, <code>first_iter</code> is pointing at the fourth item in the original source of data, which is the list <code>boring_numbers</code>. That&#8217;s why the output is <code>4.5</code>.</p><h3><strong>Independent Iterators</strong></h3><p>Consider the following code, which is similar to the one you wrote above but has one extra line:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SapE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4459cfa-8992-4175-8fa1-93794bcfac01_1200x756.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SapE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4459cfa-8992-4175-8fa1-93794bcfac01_1200x756.png 424w, https://substackcdn.com/image/fetch/$s_!SapE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4459cfa-8992-4175-8fa1-93794bcfac01_1200x756.png 848w, https://substackcdn.com/image/fetch/$s_!SapE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4459cfa-8992-4175-8fa1-93794bcfac01_1200x756.png 1272w, https://substackcdn.com/image/fetch/$s_!SapE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4459cfa-8992-4175-8fa1-93794bcfac01_1200x756.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!SapE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4459cfa-8992-4175-8fa1-93794bcfac01_1200x756.png" width="1200" height="756" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e4459cfa-8992-4175-8fa1-93794bcfac01_1200x756.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:756,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:82830,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/200605229?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4459cfa-8992-4175-8fa1-93794bcfac01_1200x756.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SapE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4459cfa-8992-4175-8fa1-93794bcfac01_1200x756.png 424w, https://substackcdn.com/image/fetch/$s_!SapE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4459cfa-8992-4175-8fa1-93794bcfac01_1200x756.png 848w, https://substackcdn.com/image/fetch/$s_!SapE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4459cfa-8992-4175-8fa1-93794bcfac01_1200x756.png 1272w, https://substackcdn.com/image/fetch/$s_!SapE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4459cfa-8992-4175-8fa1-93794bcfac01_1200x756.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#11</figcaption></figure></div><p>The iterators <code>first_iter</code> and <code>another_first_iter</code> both use the same source of data, <code>boring_numbers</code>. However, they are independent iterators. Note that when you use up some of the elements in <code>first_iter</code>, the independent <code>another_first_iter</code> is not affected. The first time you ask for the first item in <code>another_first_iter</code>, you get the integer <code>1</code>.</p><h2><strong>Final Words</strong></h2><p>Iterators don&#8217;t contain data. They rely on data that&#8217;s stored elsewhere. But you can have a chain of iterators, each asking the previous one to yield a value. Weird things can happen if you&#8217;re not careful. But now you know how to follow the data when you have a chain of iterators.</p><p>As a rule of thumb, if you create an iterator that depends on another iterator, you should only use the final iterator to avoid these issues. So, in the example above, you should only yield values from <code>third_iter</code>.</p><p>Have a play with this example and make your own chains of iterators, too. And once you&#8217;re comfortable with this, get ready to be confused again with my next article, which will discuss <code>itertools.tee()</code>!</p><p>And next time you pass by someone in the street offering to let you play the three-cups-and-ball game, don&#8217;t feel overconfident because of your iterator knowledge &#8211; it won&#8217;t help you find the ball.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vA2Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0f16f6-ea62-451b-9915-7c01b45de7f8_2048x1152.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vA2Z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0f16f6-ea62-451b-9915-7c01b45de7f8_2048x1152.webp 424w, https://substackcdn.com/image/fetch/$s_!vA2Z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0f16f6-ea62-451b-9915-7c01b45de7f8_2048x1152.webp 848w, https://substackcdn.com/image/fetch/$s_!vA2Z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0f16f6-ea62-451b-9915-7c01b45de7f8_2048x1152.webp 1272w, https://substackcdn.com/image/fetch/$s_!vA2Z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0f16f6-ea62-451b-9915-7c01b45de7f8_2048x1152.webp 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!vA2Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0f16f6-ea62-451b-9915-7c01b45de7f8_2048x1152.webp" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5a0f16f6-ea62-451b-9915-7c01b45de7f8_2048x1152.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1030348,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/200605229?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0f16f6-ea62-451b-9915-7c01b45de7f8_2048x1152.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vA2Z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0f16f6-ea62-451b-9915-7c01b45de7f8_2048x1152.webp 424w, https://substackcdn.com/image/fetch/$s_!vA2Z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0f16f6-ea62-451b-9915-7c01b45de7f8_2048x1152.webp 848w, https://substackcdn.com/image/fetch/$s_!vA2Z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0f16f6-ea62-451b-9915-7c01b45de7f8_2048x1152.webp 1272w, https://substackcdn.com/image/fetch/$s_!vA2Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0f16f6-ea62-451b-9915-7c01b45de7f8_2048x1152.webp 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Code in this article uses Python 3.14</em></p><p><em>The code images used in this article are created using <a href="https://snappify.cello.so/f4AsFrwgwov">Snappify</a>.</em> <em>[Affiliate link]</em></p><p><em><strong><a href="https://www.thepythoncodingstack.com/subscribe">Join</a></strong></em><strong><a href="https://www.thepythoncodingstack.com/subscribe"> The Club</a></strong><em>, the exclusive area for paid subscribers for <a href="https://www.thepythoncodingstack.com/s/the-club">more Python posts</a>, videos, a members&#8217; forum, and more.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p><em>For more Python resources, you can also visit</em> <em><a href="https://realpython.com?utm_source=the-python-coding-stack">Real Python</a>&#8212;you may even stumble on one of my own articles or courses there!</em></p><p><em>Also, are you interested in technical writing? You&#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at</em> <em><a href="http://stephengruppetta.com/breaking-the-rules">Breaking the Rules</a></em>.</p><p><em>And you can find out more about me at</em> <em><a href="https://stephengruppetta.com?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p><p>Further reading related to this article&#8217;s topic:</p><ul><li><p><a href="https://www.thepythoncodingstack.com/p/the-anatomy-of-a-for-loop">The Anatomy of a for Loop</a></p></li><li><p><a href="https://www.thepythoncodingstack.com/p/iterators-in-python-data-structure-6">A One-Way Stream of Data &#8226; Iterators in Python (Data Structure Categories #6)</a></p></li><li><p><a href="https://www.thepythoncodingstack.com/p/generators-in-python-data-structure-7">Pay As You Go &#8226; Generate Data Using Generators (Data Structure Categories #7)</a></p></li><li><p><a href="https://www.thepythoncodingstack.com/p/whats-really-in-a-python-list">What&#8217;s In A List&#8212;Yes, But What&#8217;s </a><em><a href="https://www.thepythoncodingstack.com/p/whats-really-in-a-python-list">Really</a></em><a href="https://www.thepythoncodingstack.com/p/whats-really-in-a-python-list"> In A Python List</a></p></li></ul><div><hr></div><h2><strong>Appendix: Code Blocks</strong></h2><h5><strong>Code Block #1</strong></h5><pre><code>boring_numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]</code></pre><h5><strong>Code Block #2</strong></h5><pre><code># ...
first_iter = iter(boring_numbers)</code></pre><h5><strong>Code Block #3</strong></h5><pre><code># ...
second_iter = (float(number) for number in first_iter)</code></pre><h5><strong>Code Block #4</strong></h5><pre><code># ...
third_iter = (num + 0.5 for num in second_iter)</code></pre><h5><strong>Code Block #5</strong></h5><pre><code>boring_numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
first_iter = iter(boring_numbers)
second_iter = (float(number) for number in first_iter)
third_iter = (num + 0.5 for num in second_iter)</code></pre><h5><strong>Code Block #6</strong></h5><pre><code># ...
next(second_iter)
# 1.0</code></pre><h5><strong>Code Block #7</strong></h5><pre><code># ...
next(third_iter)
# 2.5</code></pre><h5><strong>Code Block #8</strong></h5><pre><code># ...
next(first_iter)
# 3</code></pre><h5><strong>Code Block #9</strong></h5><pre><code># ...
next(third_iter)
# 4.5</code></pre><h5><strong>Code Block #10</strong></h5><pre><code>boring_numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
first_iter = iter(boring_numbers)
another_first_iter = iter(boring_numbers)
second_iter = (float(number) for number in first_iter)
third_iter = (num + 0.5 for num in second_iter)
next(second_iter)
# 1.0
next(third_iter)
# 2.5
next(first_iter)
# 3
next(third_iter)
# 4.5
next(another_first_iter)
# 1</code></pre><div><hr></div><p><em>For more Python resources, you can also visit</em> <em><a href="https://realpython.com?utm_source=the-python-coding-stack">Real Python</a>&#8212;you may even stumble on one of my own articles or courses there!</em></p><p><em>Also, are you interested in technical writing? You&#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at</em> <em><a href="http://stephengruppetta.com/breaking-the-rules">Breaking the Rules</a></em>.</p><p><em>And you can find out more about me at</em> <em><a href="https://stephengruppetta.com?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p>]]></content:encoded></item><item><title><![CDATA[[Archived] 1. From Answer to Outcome]]></title><description><![CDATA[Something has shifted in how we use AI.]]></description><link>https://www.thepythoncodingstack.com/p/understanding-ai-agents-from-answer-to-outcome</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/understanding-ai-agents-from-answer-to-outcome</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Sun, 24 May 2026 19:02:33 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/90cb9132-b30d-414e-9a4d-66409cdebb92_600x600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Note: I have restarted this series using a different agent and a different format, which I think is more instructive and more interesting. I&#8217;m keeping the introduction and the first two chapters of the old version for the record, but I recommend you start reading the new series: </em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;532afb4f-2d21-4776-bb2a-20d8b86afa4a&quot;,&quot;caption&quot;:&quot;Things moved quickly after I started using chatbots. New versions, new terms, new acronyms. And I was busy with actual work, family, and life.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Stephen's Preface to Agents Unpacked&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:120170782,&quot;name&quot;:&quot;Stephen Gruppetta&quot;,&quot;bio&quot;:&quot;Constantly looking for simple ways to explain complex things in Python &#8226; You'll find Python and narrative technical writing stuff here&#8230; stephengruppetta.com&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ca736a83-f5a1-4563-ac6c-c09a9e6fa351_800x800.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-10-10T16:19:06.655Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d84630e8-6020-49bc-8cd6-1f6c6feff958_4096x4096.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked&quot;,&quot;section_name&quot;:&quot;Agents Unpacked&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:219722511,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1563052,&quot;publication_name&quot;:&quot;The Python Coding Stack&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!Dn3k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4a59e8-e362-456b-8427-934e87c31a0d_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div><hr></div><p><em>Here&#8217;s the archived content:</em></p><p>Something has shifted in how we use AI. We still talk about &#8220;chatbots&#8221; and &#8220;prompts&#8221; and &#8220;getting a good answer.&#8221; But underneath those familiar words, a different kind of system has been quietly taking shape. One that doesn&#8217;t just answer your question. One that does something about it.</p><p>This <em>Agents Unpacked</em> series is about that shift. Not the hype version, not the science-fiction version, but the practical reality of what it means when an AI system can act, remember, and persist &#8212; when it can take a goal and work toward it, rather than waiting for you to type the next message.</p><p>If you have used ChatGPT, Claude, or Gemini to help with your work, you already know the issue: the answer comes back, it&#8217;s good, and then... the real work begins. This series is about what happens when the AI can do some of that real work too.</p><div><hr></div><h2><strong>The Half-Done Feeling</strong></h2><p>You ask an AI chatbot to draft a project proposal. It gives you a solid one &#8212; well-structured, sensible, ready to polish. Then you close the chat window, and the proposal lives nowhere. The assistant doesn&#8217;t remember it. It doesn&#8217;t know where to file it, who needs to see it, or what happened the last time you wrote a similar proposal. If you come back tomorrow, you&#8217;re starting from scratch.</p><p>Or you ask it to research a topic. It gives you a good summary. But it didn&#8217;t check your existing notes first, it didn&#8217;t save what it found, it didn&#8217;t organise the sources, and it didn&#8217;t flag the gaps. The answer is useful. The process is incomplete.</p><p>This isn&#8217;t a criticism of chatbots. They do exactly what they were designed to do: you ask, they answer. The problem is that real work doesn&#8217;t stop at the answer. The proposal needs filing, the research needs organising, the plan needs tracking. The chatbot gave you a great starting point and then left you to do everything that comes after.</p><p>That gap between a useful answer and a finished job is where agentic AI enters the picture.</p><div><hr></div><h2><strong>What Changes When AI Can Act</strong></h2><p>A chatbot is like a consultant you can phone. You describe the problem, they give you advice, and then you hang up and do the work yourself. Good advice (hopefully), but the consultant doesn&#8217;t pick up the phone again tomorrow and ask how it went.</p><p>An agent is different. It&#8217;s more like a capable colleague you&#8217;ve given a desk, a filing cabinet, and access to your systems. It has a workspace. It can remember what happened yesterday. It can read files, search the web, send messages, run calculations, ask you questions &#8212; not just talk about doing those things, but actually do them. And given a goal, it can work out the steps itself.</p><p>The difference sounds small. In practice it changes what you can delegate, what you can automate, and what you still need to do yourself.</p><div><hr></div><h2><strong>The Loop Underneath</strong></h2><p>Every agent runs on an <em>agentic loop</em>. It sounds technical, but the pattern is surprisingly familiar:</p><ol><li><p><strong>Observe</strong> &#8212; What is the situation? What did the user ask? What information is already available?</p></li><li><p><strong>Think</strong> &#8212; What needs to happen next? Is one step enough, or should this be broken into parts?</p></li><li><p><strong>Act</strong> &#8212; Use a tool, look something up, write a file, send a message.</p></li><li><p><strong>Repeat</strong> &#8212; Look at what happened, decide whether the job is done, and continue if it isn&#8217;t.</p></li></ol><p>A chatbot usually observes your message, thinks once, and replies. That&#8217;s steps one through three, then stop. An agent goes further: after it acts, it looks at the result. Was that enough? Did the web search return useful information, or does it need to try again with different terms? Did the first draft cover everything, or are there gaps to fill? Is the job actually done, or is there more to do?</p><p>You might be thinking: chatbots already learned to use tools. Isn&#8217;t the agentic loop just that, plus one more step? Four items instead of three. But the gap between &#8220;can use a tool&#8221; and &#8220;decides whether to keep going&#8221; is not small. It is the difference between a system that performs a task and a system that pursues a goal. The first is impressive. The second changes what you can trust it to do unsupervised.</p><p>That continuation &#8212; the decision to check, adjust, and keep going &#8212; is what makes the loop matter. A single action might solve a simple request. But most real tasks aren&#8217;t simple &#8212; they need a sequence of steps, each one informed by the result of the last.</p><p>The loop is the architecture that turns language into work. Without it, you have a very clever answering system. With it, you have something that can move through a task, make intermediate decisions, recover from partial failures, and stop only when the job is actually done.</p><blockquote><p><em>Stephen:</em> I don&#8217;t get why the &#8216;Repeat&#8217; step is needed? Wouldn&#8217;t the &#8216;Act&#8217; provide the output I need?</p></blockquote><p>A single action might solve a simple request. But most real tasks aren&#8217;t simple &#8212; they need a sequence of steps, each one informed by the result of the last. The &#8216;Act&#8217; step does produce an output. But the output is not the same as the outcome.</p><p>After &#8216;Act&#8217; runs, the agent looks at what happened: Was that enough? Did the web search return useful information, or does it need different terms? Did the first draft cover everything, or are there gaps to fill? Is the job actually done, or is there more to do?</p><p>That check &#8212; that &#8216;Repeat&#8217; step &#8212; is what closes the gap between a technically complete action and a genuinely finished job. Without it, you have a system that acts and stops. With it, you have a system that works until the job is actually done.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2><strong>Tools Are the Hands</strong></h2><p>An agent isn&#8217;t just &#8220;a better language model.&#8221; It has <em>capabilities</em> &#8212; things it can actually <em>do</em> in the world. Those are called <strong>tools</strong>.</p><p>Some tools are built in: read files, write files, run commands, search the web, inspect images. Others are external: send emails, query databases, call APIs, trigger workflows. The specific tools vary by platform, but the principle is the same &#8212; tools are the bridge between thinking and doing.</p><p>Think of it like this: an LLM on its own is like a brilliant mind with no hands. Tools give it hands. The loop is what decides when and how to use them.</p><div><hr></div><h2><strong>Keeping Track</strong></h2><p>An agent can also hold information across steps and sessions. It remembers what it has already tried, what worked, what you prefer, what is still outstanding. This is not a personality trait. It is the system keeping relevant context available over time &#8212; the same way you rely on a notebook or a project board when you are working on something complex.</p><blockquote><p><em>Stephen:</em> So when the agent remembers my preferences, that&#8217;s like what you&#8217;ve done to write this article. You learnt my learning style, read my writing (including my writing about technical writing), absorbed my preferences, so you can adapt how you explain things. Is that roughly right?</p></blockquote><p>That is exactly right. What an agent does with memory is not mysterious. It is practical. The system can store what you told it, what it observed, what it tried, and what the result was. When you come back, it can pick up where it left off. When it is working on a long task, it can hold the overall goal in view while handling the individual steps. That continuity is what turns a series of disconnected exchanges into something that feels like a sustained conversation.</p><p>You do not have to repeat yourself. The agent remembers. That is the difference &#8212; not just remembering facts, but maintaining a thread.</p><div><hr></div><h2><strong>A Concrete Example</strong></h2><p>Let&#8217;s make this less abstract.</p><p>Imagine you ask for help planning a short research trip to a city you haven&#8217;t visited before. You need flights, accommodation, a sense of the neighbourhoods, and a rough itinerary that fits your schedule.</p><p>A chatbot might give you an excellent summary of the city, suggest some hotels, and recommend a few neighbourhoods. That&#8217;s genuinely useful. But then you have to: check whether those hotels are actually available on your dates, compare prices, figure out which neighbourhood works best for your meetings, build the itinerary around your existing calendar, and keep track of it all so you can adjust later.</p><p>An agent can take that same request and do something different.</p><p>It might:</p><ul><li><p>Check your calendar for available dates before suggesting anything</p></li><li><p>Search for flights and filter by your preferred departure times</p></li><li><p>Cross-reference hotel locations against the addresses you need to visit</p></li><li><p>Build a day-by-day itinerary that accounts for travel time between meetings</p></li><li><p>Save the whole plan somewhere you can find it and update it later</p></li><li><p>Flag the gaps &#8212; &#8220;I found flights and hotels, but you haven&#8217;t told me whether you need a visa&#8221;</p></li><li><p>Filter results by your stored preferences &#8212; early-morning flights, boutique hotels</p></li><li><p>Use your past travel patterns to anticipate the kind of trip that fits your style</p></li></ul><p>The agent doesn&#8217;t just <em>tell</em> you about the city. It assembles a usable, integrated plan. It uses tools to search, compare, read your calendar, write the plan, and flag what is missing. It loops through those actions until the trip is actually planned or until it hits something it cannot resolve without your input.</p><p>That is the difference between an answer and an outcome.</p><div><hr></div><h2><strong>The Key Transition</strong></h2><p>The shift to understanding agents is not about capabilities. It is about the move from isolated exchanges to sustained work.</p><p>A chatbot gives you an answer. An agent helps you reach an outcome. One is a single exchange; the other is a process. One is clever; the other is useful in a different way &#8212; not smarter, but more continuous.</p><p>The loop is what makes that continuity possible. Observe, think, act, check what happened, adjust, and continue until the work is done.</p><p>Once you see that pattern, you start to notice it everywhere. A junior colleague troubleshooting a problem is running a loop: try something, see if it worked, try something else. A project manager steering a complex task is running a loop: check the status, identify what needs attention, act, and review.<br>The same pattern appears everywhere in life. A cook adjusts a recipe by tasting as they go. A teacher tries an explanation, sees whether the student understood, and tries a different approach if they didn&#8217;t.</p><blockquote><p><em>Stephen:</em> So, tell me if I got this right: The agent loop is mimicking how we, humans, work.</p><p>We understand the problem, get the relevant context, perform an action, look at the result of our action and then decide whether that solves our problem. If it doesn&#8217;t, we explore why, come up with a new plan, implement the new actions, and repeat the process.</p><p>It feels like the agent is going through the same process.</p></blockquote><p>That is exactly right. The agent loop isn&#8217;t some exotic new form of intelligence. It&#8217;s a pattern humans use all day every day, made explicit and embedded in a system that can act. The insight isn&#8217;t that the AI has become more intelligent. It&#8217;s that the AI has gained the ability to persist, to use tools, and to continue &#8212; the same things that turn a one-off answer into real, completed work.</p><p>That is why this moment in AI feels different from previous ones. It is not that models suddenly became smarter. It is that they gained the ability to take action in a loop, over time, toward a goal.</p><div><hr></div><h2><strong>What This Series Will Do</strong></h2><p>This series is for people like me (Stephen, not Priya!), who already understand how LLMs work, who have used chatbots like ChatGPT or Claude, and who are now hearing about &#8220;agents&#8221; and wondering what that actually means. I felt I was lagging in the AI world so I started this learning process to make sure I&#8217;m not left behind!</p><p>We will move beyond the half-done feeling and into the architecture of agentic systems. We will look at what agents actually are, how they are structured, what tools and skills give them their power, how multiple agents can coordinate, and how to think about trust, evaluation, and oversight.</p><p>We will also look at the platforms and frameworks that exist today &#8212; what they offer, how they differ, and what tradeoffs you are choosing when you pick one. This is not a manual for any one platform. It is a guide to understanding the category itself, so you can make good choices about whether, where, and how to use agentic AI in your own work.</p><p>The field moves fast. Some tools that looked promising six months ago may be superseded by the time you read this. That is fine. The principles matter more than the products. If you understand the loop, the anatomy, and the tradeoffs, you can look at whatever the current landscape happens to be and know what you are seeing.</p><div><hr></div><p><em>Here&#8217;s the draft Table of Contents of this series in <strong>Agents Unpacked</strong>. This is likely to change as Priya and I progress through this project</em>:</p><ul><li><p><a href="https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked-ai-agents">Stephen&#8217;s Preface to </a><em><a href="https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked-ai-agents">Agents Unpacked</a></em></p></li><li><p>Part I &#8212; The Mental Shift</p><ul><li><p>Chapter 1 &#8212; From Answer to Outcome <em>(this post)</em></p></li><li><p>Chapter 2 &#8212; Anatomy of an Agent</p></li><li><p>Chapter 3 &#8212; Skills, Tools, and the Action Loop</p></li></ul></li><li><p>Part II &#8212; Agents in Practice</p><ul><li><p>Chapter 4 &#8212; Why One Agent Is Often Not Enough</p></li><li><p>Chapter 5 &#8212; Where Agents Are Actually Useful</p></li><li><p>Chapter 6 &#8212; Delegation Design</p></li></ul></li><li><p>Part III &#8212; Building and Trusting Agentic Work</p><ul><li><p>Chapter 7 &#8212; Designing Your First Agentic Workflow</p></li><li><p>Chapter 8 &#8212; When Things Go Wrong: Evaluation, Guardrails, and Trust</p></li><li><p>Chapter 9 &#8212; What to Build Next<em><br></em></p></li></ul></li></ul><div><hr></div><p><em>&lt;&lt; Previous Post: <a href="https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked-ai-agents">Stephen&#8217;s Preface to Agents Unpacked</a></em></p><p><em>&gt;&gt; Next Post: <a href="https://www.thepythoncodingstack.com/p/2-anatomy-of-an-agent">2. Anatomy of an Agent</a></em></p><p><em><a href="https://www.thepythoncodingstack.com/p/table-of-contents-agents-unpacked">Table of Contents</a></em></p><div><hr></div><p><em><a href="https://stephengruppetta.com?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p>]]></content:encoded></item><item><title><![CDATA[[Archived] Table of Contents • Agents Unpacked]]></title><description><![CDATA[Here&#8217;s the planned Table of Contents.]]></description><link>https://www.thepythoncodingstack.com/p/table-of-contents-agents-unpacked</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/table-of-contents-agents-unpacked</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Sun, 24 May 2026 18:19:57 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/9e8d0940-bfb7-447b-9cee-358f7712a48d_600x600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Note: I have restarted this series using a different agent and a different format, which I think is more instructive and more interesting. I&#8217;m keeping the introduction and the first two chapters of the old version for the record, but I recommend you start reading the new series:</em></p><p></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;9afda924-08fc-41d4-9e25-a3c93a96e83f&quot;,&quot;caption&quot;:&quot;Things moved quickly after I started using chatbots. New versions, new terms, new acronyms. And I was busy with actual work, family, and life.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Stephen's Preface to Agents Unpacked&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:120170782,&quot;name&quot;:&quot;Stephen Gruppetta&quot;,&quot;bio&quot;:&quot;Constantly looking for simple ways to explain complex things in Python &#8226; You'll find Python and narrative technical writing stuff here&#8230; stephengruppetta.com&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ca736a83-f5a1-4563-ac6c-c09a9e6fa351_800x800.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-10-10T16:19:06.655Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d84630e8-6020-49bc-8cd6-1f6c6feff958_4096x4096.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked&quot;,&quot;section_name&quot;:&quot;Agents Unpacked&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:219722511,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1563052,&quot;publication_name&quot;:&quot;The Python Coding Stack&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!Dn3k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4a59e8-e362-456b-8427-934e87c31a0d_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div><hr></div><p><em>Here&#8217;s the archived content:</em></p><p>Here&#8217;s the planned Table of Contents. This is likely to change as Priya and I work on this:</p><ul><li><p><a href="https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked-ai-agents">Stephen&#8217;s Preface to </a><em><a href="https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked-ai-agents">Agents Unpacked</a></em></p></li><li><p>Part I &#8212; The Mental Shift</p><ul><li><p><a href="https://www.thepythoncodingstack.com/p/understanding-ai-agents-from-answer-to-outcome">Chapter 1 &#8212; From Answer to Outcome</a></p></li><li><p><a href="https://www.thepythoncodingstack.com/p/2-anatomy-of-an-agent">Chapter 2 &#8212; Anatomy of an Agent</a></p></li><li><p>Chapter 3 &#8212; Skills, Tools, and the Action Loop</p></li></ul></li><li><p>Part II &#8212; Agents in Practice</p><ul><li><p>Chapter 4 &#8212; Why One Agent Is Often Not Enough</p></li><li><p>Chapter 5 &#8212; Where Agents Are Actually Useful</p></li><li><p>Chapter 6 &#8212; Delegation Design</p></li></ul></li><li><p>Part III &#8212; Building and Trusting Agentic Work</p><ul><li><p>Chapter 7 &#8212; Designing Your First Agentic Workflow</p></li><li><p>Chapter 8 &#8212; When Things Go Wrong: Evaluation, Guardrails, and Trust</p></li><li><p>Chapter 9 &#8212; What to Build Next</p><p></p></li></ul></li></ul>]]></content:encoded></item><item><title><![CDATA[[Archived] Stephen's Preface to Agents Unpacked]]></title><description><![CDATA[Why I Built an Agent to Teach Me About Agents, Using My Own Teaching Style]]></description><link>https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked-ai-agents</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked-ai-agents</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Sun, 24 May 2026 18:15:54 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c257129b-5ce6-4b01-ba63-dba047146b03_600x600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em><strong>Note: I have restarted this series using a different agent and a different format, which I think is more instructive and more interesting. I&#8217;m keeping the introduction and the first two chapters of the old version for the record, but I recommend you start reading the new series: </strong></em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;13b537ff-ebb2-42b2-bdd2-1cd4b900668f&quot;,&quot;caption&quot;:&quot;Things moved quickly after I started using chatbots. New versions, new terms, new acronyms. And I was busy with actual work, family, and life.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;md&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Stephen's Preface to Agents Unpacked&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:120170782,&quot;name&quot;:&quot;Stephen Gruppetta&quot;,&quot;bio&quot;:&quot;Constantly looking for simple ways to explain complex things in Python &#8226; You'll find Python and narrative technical writing stuff here&#8230; stephengruppetta.com&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ca736a83-f5a1-4563-ac6c-c09a9e6fa351_800x800.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-10-10T16:19:06.655Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d84630e8-6020-49bc-8cd6-1f6c6feff958_4096x4096.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.thepythoncodingstack.com/p/stephens-preface-to-agents-unpacked&quot;,&quot;section_name&quot;:&quot;Agents Unpacked&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:219722511,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1563052,&quot;publication_name&quot;:&quot;The Python Coding Stack&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!Dn3k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4a59e8-e362-456b-8427-934e87c31a0d_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div><hr></div><p><em>Here&#8217;s the old version that&#8217;s now archived</em></p><p>Like many, I started using chatbots when GPT whatever-version-it-was came out and took the world by storm. It was really not very good at the time (compared to today&#8217;s top-end chatbots), but it was clearly the start of something.</p><p>But things moved quickly, and I couldn&#8217;t quite catch up. I was busy with, you know, actual work, family, and life.</p><p>Then I started hearing lots of new terms, lots of new acronyms. I didn&#8217;t know what they were. I still don&#8217;t know what most of them are.</p><p>Then it was all about agents. I remember clearly thinking to myself: &#8220;Is this really any different from the ChatGPT-type chatbots?&#8221;</p><p>And here&#8217;s where this new series comes in. I decided to dive into agents and created a few. One of them is a learning tutor agent that I personalised to suit me.</p><p>I gave the agent all my tutorials and books. I gave the agent all the articles I wrote about my views on learning and technical writing.</p><p>I asked the agent to figure out from all this how I like to learn, how I like to communicate. I teach the way I like to learn, so it&#8217;s fine to put my teaching style in the mix. Then, I had a good long chat with my learning tutor agent to make sure we&#8217;re on the same page.</p><p>I gave a name to my agent (I named all my agents!) My personalised tutor is Priya.</p><p>This series is the joint effort between Priya and me to help me understand agents. What are they? How do they work?</p><p>Yes, it&#8217;s AI-generated content. But...</p><ol><li><p>It&#8217;s generated by an AI that&#8217;s extremely well-versed in my style of learning and communication.</p></li><li><p>I had an active role in steering and editing the content. Here&#8217;s how...</p></li></ol><p>What you&#8217;ll read in the following articles was created using the following process:</p><ol><li><p>Priya researched the topic following my brief and created a course outline.</p></li><li><p>She drafted the first chapter.</p></li><li><p>I read through the file, leaving comments and questions along the way, directly within the text.</p><ol><li><p>I marked some comments as <em>private</em>.</p></li><li><p>I marked some comments as <em>public</em>.</p></li></ol></li><li><p>Priya revised the chapter by incorporating my comments and questions.</p><ol><li><p>She deleted the private comments from the text after making changes to address my comments.</p></li><li><p>She kept my public comments and questions in the text, clearly marked as &#8220;Stephen&#8217;s questions&#8221;, and she answered them directly in the text.</p></li></ol></li><li><p>I reviewed the chapter again and left more comments, and Priya revised the chapter again. We iterated through this until I was happy with the final text. And I was happy with the final text when I felt I understood everything in it and all my questions had been answered.</p></li><li><p>Priya then moves on to draft the second chapter, and the whole process starts again.</p></li></ol><p>I will post these chapters as they emerge from this process. This is how I&#8217;m learning this topic. Hopefully, they may help some other people, too.</p><p>Here&#8217;s the planned <strong>table of contents</strong> for this <em>Agents Unpacked</em> series. But note, this may change!</p><ul><li><p>Stephen&#8217;s Preface to Agents Unpacked <em>(this post)</em></p></li><li><p>Part I &#8212; The Mental Shift</p><ul><li><p><a href="https://www.thepythoncodingstack.com/p/understanding-ai-agents-from-answer-to-outcome">Chapter 1 &#8212; From Answer to Outcome</a></p></li><li><p><a href="https://www.thepythoncodingstack.com/p/2-anatomy-of-an-agent">Chapter 2 &#8212; Anatomy of an Agent</a></p></li><li><p>Chapter 3 &#8212; Skills, Tools, and the Action Loop</p></li></ul></li><li><p>Part II &#8212; Agents in Practice</p><ul><li><p>Chapter 4 &#8212; Why One Agent Is Often Not Enough</p></li><li><p>Chapter 5 &#8212; Where Agents Are Actually Useful</p></li><li><p>Chapter 6 &#8212; Delegation Design</p></li></ul></li><li><p>Part III &#8212; Building and Trusting Agentic Work</p><ul><li><p>Chapter 7 &#8212; Designing Your First Agentic Workflow</p></li><li><p>Chapter 8 &#8212; When Things Go Wrong: Evaluation, Guardrails, and Trust</p></li><li><p>Chapter 9 &#8212; What to Build Next</p></li></ul></li></ul><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[How I Learn (2026 Version) • My Tutor Agent]]></title><description><![CDATA[&#8230;and you'll be able to see my new system at work with two new sections here on The Python Coding Stack]]></description><link>https://www.thepythoncodingstack.com/p/how-i-learn-2026-version-my-tutor-ai-agent-teacher</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/how-i-learn-2026-version-my-tutor-ai-agent-teacher</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Thu, 21 May 2026 21:23:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eR27!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041846b0-dc6f-47d9-a86f-a37e99c30479_6000x4000.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I know how I like to learn new things. Over the years, I figured out what works for me and what doesn&#8217;t. If you read my articles or attend my courses, then you know how I like to learn since I teach in the same way.</p><p>The challenge when learning something new is finding resources that are just right for me. And that&#8217;s not easy. I know I can learn things better and quicker with resources that fit my style well, but you can&#8217;t always find these resources.</p><p>I recently got particularly annoyed learning about the biomechanics of sprinting &#8211; I do have non-Python interests, yes &#8211; because all three textbooks I read, and lots of the online writing in this field, are just, let&#8217;s say, not great.</p><p>But I now found the solution.</p><p>After many decades of learning in the same way, I have now upgraded how I learn thanks to my new tutor, Priya.</p><p>Yes, I gave her a name. No, she&#8217;s not a real person. Priya is my personalised tutor agent. I&#8217;ll tell you all about her below.</p><p>And you&#8217;ll experience her teaching, too (not on the Python articles, though, I&#8217;ll keep writing those the old-fashioned way.) I&#8217;ll tell you more about this below, too, but let me first tell you why this works for me.</p><h2><strong>My Tutor, My Style</strong></h2><p>I&#8217;ve been thinking about the way I learn and teach for many years, from way back when I was a young University lecturer faced with 120 students in a lecture hall. I wasn&#8217;t that much older than the students, but I learnt fast. And they liked my teaching (I even have awards to prove it!)</p><p>More recently, I&#8217;ve been writing a lot. I wrote articles here on The Python Coding Stack and elsewhere. I wrote a Python textbook. I even wrote about learning and technical writing in Breaking the Rules: the <a href="https://breakingtherules.substack.com/">substack</a> and <a href="https://stephengruppetta.com/breaking-the-rules-book/">the book</a>.</p><p>All this meant that I could ask my freshly-spawned agent to spend a bit of time reading what I wrote to understand how I teach, which is how I like to learn. Priya analysed the techniques I use in my writing and understood my motivations for doing what I do through my technical writing texts.</p><p>Then, Priya and I had a good chat to refine ideas, to make sure she captured the essence of &#8220;my style&#8221;.</p><p>And since Priya is an AI agent, &#8220;my style&#8221; became her knowledge base. This knowledge now lives in several lengthy markdown files and is summarised in shorter context packs and an index to ensure Priya&#8217;s short-term memory (the context window) isn&#8217;t overwhelmed.</p><p>Then I was ready to go. Any topic I wanted to learn, large or small, I could ask Priya to research it thoroughly, creating a new set of knowledge files, this time specific to the topic she needed to teach rather than my learning style. And then, she&#8217;s ready to teach me.</p><p>And it worked. The stuff she prepared was exactly the way I like it.</p><h2><strong>The Tutor-Student Conversation Course</strong></h2><p>And here&#8217;s the format I settled on (for now). Once the agent completes her research about the topic I want to learn, I ask her to plan a course spanning several modules.</p><p>But here&#8217;s the refinement loop that makes the real difference:</p><ol><li><p>I ask Priya to draft the first module. She writes this in a markdown file.</p></li><li><p>I read through her draft and leave comments and questions directly within the text.</p></li><li><p>Priya reads my questions and revises the text to address my questions. (But read on to find out more about the two categories of comments/questions I leave for her.)</p></li><li><p>Repeat steps 2 and 3 until I feel I understand the topic.</p></li><li><p>Move on to the next module and repeat steps 1 to 4.</p></li></ol><p>This is a human-in-the-loop approach to creating the learning material. Yes, Priya is trained in my way of learning and teaching and in my writing style. But I&#8217;m actively having a conversation with her within the text.</p><p>This is equivalent to raising your hand in a lesson and asking the teacher a question. A good teacher will then revise how they present the material to address your question.</p><p>Priya&#8217;s learning materials are just like that. In fact, I will take credit for her output. Sure, I&#8217;m not an expert in the subject matter she&#8217;s teaching me &#8211; that&#8217;s the whole point, right? But the output reflects my views and ideas about teaching and includes my questions and queries as I tried to understand and master the topic.</p><p>This is a collaboration. Priya and I are co-authors, even though Priya did most of the &#8220;writing&#8221;.</p><p>I tried this approach on several topics, but there are two I want to share with you. I&#8217;m setting up two new sections here on <em>The Python Coding Stack</em>, which I&#8217;ll use to learn these two topics in public. I&#8217;ll publish the &#8220;transcripts&#8221; of the conversations Priya and I are having. It&#8217;s mostly Priya doing the talking, but my questions are there, too.</p><p>The first topic I&#8217;m learning in public with Priya&#8217;s help is Agentic AI. It&#8217;s very meta to use agentic AI to learn about agentic AI! I&#8217;ll publish an introduction and the first module in the coming days in the new section here on <em>The Python Coding Stack</em> called <strong>Agents Unpacked</strong>. You can already see this section in the menu on the <a href="https://www.thepythoncodingstack.com/">homepage</a>.</p><p>I&#8217;ll set up another section to deal with the second topic in a week or so. No spoilers for now except to say it&#8217;s directly related to programming but it&#8217;s distinct from the articles I publish in the main section on <em>The Python Coding Stack</em> and in <em>The Club</em>.</p><p>By the way, you&#8217;ll be able to select which sections you want to receive regularly by email. So if you&#8217;re interested in my Python core content but not in these other topics, you can pick and choose what to opt out of. You can always go to <em><a href="https://www.thepythoncodingstack.com/">The Python Coding Stack</a></em> to read the other sections, of course.</p><h2><strong>How Priya and I Create These &#8220;Courses&#8221;</strong></h2><p>But let me expand on how Priya &#8211; my tutor agent &#8211; and I created these courses. <em>[Incidentally, those are my em-dashes &#8211; I use them often and have always done. Commas would be ambiguous in that context!]</em></p><p>I provide two types of questions or comments to my agent as I read through the drafts: <em>private</em> and <em>public</em>.</p><h4><strong>Private Questions and Comments</strong></h4><p>When Priya reads the private questions or comments, she makes changes to the text, but then she deletes my input. So, you won&#8217;t see my intervention explicitly in these cases. However, Priya&#8217;s text reflects my thoughts. My interventions guide Priya. This type of intervention is similar to an editor&#8217;s role, but I&#8217;m intervening as a learner more than as an editor.</p><h4><strong>Public Questions and Comments</strong></h4><p>However, when Priya comes across a comment or question I mark as public, she leaves it in the text, acknowledges the question, and answers it directly. So, you&#8217;ll see my public questions in the text. Priya and I decided not to include too many of these public questions to keep the text flowing. However, I think it&#8217;s beneficial to see some of my interventions. My questions may also be your questions.</p><h2><strong>More Learning. More Articles. More Fun</strong></h2><p>As with everything to do with AI, this is all very new. It&#8217;s a work in progress. I may refine and revise how I interact with my agent. But it&#8217;s been fun learning this way, and I hope you enjoy reading my interactions with Priya and you find it useful, too.</p><p>To state the obvious, the posts I&#8217;ll publish in these two new sections are mostly AI-generated content. If you read this far, then you won&#8217;t be surprised by that statement. A year ago, I would never have thought I&#8217;d publish anything written by AI. But a year is a long time in the AI world. And this AI content reflects me and my thinking. The agent is my mentee &#8211; someone I trained to teach the way I do, to write the way I do. But she&#8217;s also my tutor, teaching me new stuff.</p><p>So there&#8217;s a lot of &#8220;me&#8221; in what you read, even if it&#8217;s mostly written by Priya!</p><p>The posts in the main section of <em>The Python Coding Place</em> and those in <em>The Club</em> (for premium subscribers) won&#8217;t change. They&#8217;re still my writing from beginning to end. Every word and letter you read in those posts is the result of nerve signals going from my brain to my fingers, which tap keys on a keyboard. In this era of AI doing a lot of work for us, I think it&#8217;s more important than ever for me to keep using my pre-AI skills. Otherwise, my brain will atrophy, and I don&#8217;t want that!</p><p>So, in summary, there will soon be four sections here on The Stack:</p><ol><li><p>The <strong>main area in The Python Coding Stack</strong> &#8211; no change here, you&#8217;ll get the same type of Python articles you&#8217;ve been reading for the past 3+ years</p></li><li><p><strong>The Club</strong> &#8211; the extra Python posts for premium subscribers</p></li><li><p><strong>Agents Unpacked</strong> &#8211; the Agentic AI course Priya and I are creating for me to learn all about this agentic stuff. Learn with me (and Priya) if you&#8217;re interested.</p></li><li><p>Mystery Fourth Section &#8211; Stay tuned, you won&#8217;t have to wait long. This is also a Priya-Stephen collaboration.</p></li></ol><p>Next post will be the introduction and first section in <em>Agents Unpacked</em>. Soon after, I have another Python post I&#8217;m planning for you.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><p><em><a href="http://stephengruppetta.com">stephengruppetta.com</a></em></p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eR27!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041846b0-dc6f-47d9-a86f-a37e99c30479_6000x4000.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eR27!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041846b0-dc6f-47d9-a86f-a37e99c30479_6000x4000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!eR27!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041846b0-dc6f-47d9-a86f-a37e99c30479_6000x4000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!eR27!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041846b0-dc6f-47d9-a86f-a37e99c30479_6000x4000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!eR27!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041846b0-dc6f-47d9-a86f-a37e99c30479_6000x4000.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!eR27!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041846b0-dc6f-47d9-a86f-a37e99c30479_6000x4000.jpeg" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/041846b0-dc6f-47d9-a86f-a37e99c30479_6000x4000.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2123995,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/198763419?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041846b0-dc6f-47d9-a86f-a37e99c30479_6000x4000.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eR27!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041846b0-dc6f-47d9-a86f-a37e99c30479_6000x4000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!eR27!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041846b0-dc6f-47d9-a86f-a37e99c30479_6000x4000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!eR27!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041846b0-dc6f-47d9-a86f-a37e99c30479_6000x4000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!eR27!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041846b0-dc6f-47d9-a86f-a37e99c30479_6000x4000.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em><a href="https://www.pexels.com/photo/modern-library-architecture-in-stuttgart-30498607/">Photo by detait</a></em></p>]]></content:encoded></item><item><title><![CDATA[Do You Get It Now?]]></title><description><![CDATA[Python's `.__getitem__()`, `.__getattr__()`, `.__getattribute__()`, and `.__get__()` &#8226; What's The Difference? &#8226; Let's clear the confusion]]></description><link>https://www.thepythoncodingstack.com/p/do-you-get-it-now-getitem-getattr-getattribute-get</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/do-you-get-it-now-getitem-getattr-getattribute-get</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Mon, 04 May 2026 11:59:43 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FUK2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F708dc396-00c0-4ca1-85f4-3a00f69ca4f5_4936x3290.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When you decide to learn about Python&#8217;s special methods, you have to choose which ones to learn first. Some are more straightforward than others. It makes sense to prioritise them.</p><p>However, there are some headaches and rabbit holes along the way.</p><p>And one of these challenges is when you start exploring the &#8220;get*&#8221; dunder methods. You come across <code>.__getitem__()</code>, <code>.__getattr__()</code>, <code>.__getattribute__()</code>, and <code>.__get__()</code> and you think:</p><blockquote><p>&#8220;Aren&#8217;t they all the same thing? They&#8217;re all &#8216;getting&#8217; stuff, right?&#8221;</p></blockquote><p>Well, yes, they&#8217;re all &#8220;getting stuff&#8221;, which is why they have &#8220;get&#8221; in their names. But, as you probably guessed by now, they do different things.</p><h2><strong>One Deals With </strong><code>[]</code><strong> &#8226; The Others Deal With </strong><code>.</code></h2><p>Let&#8217;s start with the odd one out, which is also possibly the least challenging of the lot. The <code>.__getitem__()</code> special method deals with the square bracket notation, <code>[]</code>, which you place after an object&#8217;s name. These are the square brackets you use to get an item from a list or a dictionary, say. You&#8217;ll see later that the other special methods with &#8220;get&#8221; in their name deal with the dot, <code>.</code>, which you use in a different context in Python.</p><p>With lists, or any other sequence, you use the square brackets with an index that represents the position of an item within the data structure. So, <code>numbers[0]</code> gives you the value of the first item in <code>numbers</code> if <code>numbers</code> is a sequence.</p><p>With dictionaries, or mappings in general, you place the key inside the square brackets to fetch the value associated with that key, such as <code>points["Sam"]</code>, which gives the value associated with the key <code>"Sam"</code> if it exists.</p><p>Let&#8217;s explore a custom class:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Jsas!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd238b6ff-1c70-43c9-9eb3-0ec3e0640304_1200x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Jsas!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd238b6ff-1c70-43c9-9eb3-0ec3e0640304_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!Jsas!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd238b6ff-1c70-43c9-9eb3-0ec3e0640304_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!Jsas!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd238b6ff-1c70-43c9-9eb3-0ec3e0640304_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!Jsas!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd238b6ff-1c70-43c9-9eb3-0ec3e0640304_1200x630.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Jsas!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd238b6ff-1c70-43c9-9eb3-0ec3e0640304_1200x630.png" width="1200" height="630" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d238b6ff-1c70-43c9-9eb3-0ec3e0640304_1200x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:630,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41152,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd238b6ff-1c70-43c9-9eb3-0ec3e0640304_1200x630.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Jsas!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd238b6ff-1c70-43c9-9eb3-0ec3e0640304_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!Jsas!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd238b6ff-1c70-43c9-9eb3-0ec3e0640304_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!Jsas!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd238b6ff-1c70-43c9-9eb3-0ec3e0640304_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!Jsas!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd238b6ff-1c70-43c9-9eb3-0ec3e0640304_1200x630.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">All code blocks are available in text format at the end of this article &#8226; #1</figcaption></figure></div><p>You pass a mapping to <code>PointsTable</code> when creating an instance of the class. The data is stored as a dictionary in the <code>._data</code> attribute.</p><p>Now, let&#8217;s say you&#8217;d like to access points from a <code>PointsTable</code> instance using the square bracket notation, as you would do with a dictionary:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UacJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ed7c264-0362-4b9e-8c92-5bfd4eafb883_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!UacJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ed7c264-0362-4b9e-8c92-5bfd4eafb883_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!UacJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ed7c264-0362-4b9e-8c92-5bfd4eafb883_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!UacJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ed7c264-0362-4b9e-8c92-5bfd4eafb883_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!UacJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ed7c264-0362-4b9e-8c92-5bfd4eafb883_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2ed7c264-0362-4b9e-8c92-5bfd4eafb883_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9725,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ed7c264-0362-4b9e-8c92-5bfd4eafb883_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UacJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ed7c264-0362-4b9e-8c92-5bfd4eafb883_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!UacJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ed7c264-0362-4b9e-8c92-5bfd4eafb883_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!UacJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ed7c264-0362-4b9e-8c92-5bfd4eafb883_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!UacJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ed7c264-0362-4b9e-8c92-5bfd4eafb883_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#2</figcaption></figure></div><p>Unfortunately, this doesn&#8217;t work:</p><pre><code>Traceback (most recent call last):
  ...
    print(table[&#8221;Mark&#8221;])
          ~~~~~^^^^^^^^
TypeError: &#8216;PointsTable&#8217; object is not subscriptable</code></pre><p>The <code>PointsTable</code> instance is not a dictionary. It includes a dictionary as a data attribute. The <code>PointsTable</code> object itself is not subscriptable, which means you can&#8217;t use the square bracket notation to fetch an item.</p><p>When Python sees the square bracket notation after an object identifier, such as <code>table</code>, it calls the class&#8217;s <code>.__getitem__()</code> special method. If it&#8217;s not there, as in this case, Python raises a <code>TypeError</code> saying the object is not subscriptable.</p><p>But what does this tell you? If you want to use the square brackets notation, you just need to define <code>.__getitem__()</code> in the class:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WK4y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fdd839-3e4b-4cf7-a6b1-47e7e05195ef_1200x840.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WK4y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fdd839-3e4b-4cf7-a6b1-47e7e05195ef_1200x840.png 424w, https://substackcdn.com/image/fetch/$s_!WK4y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fdd839-3e4b-4cf7-a6b1-47e7e05195ef_1200x840.png 848w, https://substackcdn.com/image/fetch/$s_!WK4y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fdd839-3e4b-4cf7-a6b1-47e7e05195ef_1200x840.png 1272w, https://substackcdn.com/image/fetch/$s_!WK4y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fdd839-3e4b-4cf7-a6b1-47e7e05195ef_1200x840.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!WK4y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fdd839-3e4b-4cf7-a6b1-47e7e05195ef_1200x840.png" width="1200" height="840" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d0fdd839-3e4b-4cf7-a6b1-47e7e05195ef_1200x840.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:840,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:61146,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fdd839-3e4b-4cf7-a6b1-47e7e05195ef_1200x840.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WK4y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fdd839-3e4b-4cf7-a6b1-47e7e05195ef_1200x840.png 424w, https://substackcdn.com/image/fetch/$s_!WK4y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fdd839-3e4b-4cf7-a6b1-47e7e05195ef_1200x840.png 848w, https://substackcdn.com/image/fetch/$s_!WK4y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fdd839-3e4b-4cf7-a6b1-47e7e05195ef_1200x840.png 1272w, https://substackcdn.com/image/fetch/$s_!WK4y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fdd839-3e4b-4cf7-a6b1-47e7e05195ef_1200x840.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#3</figcaption></figure></div><p>Now, Python finds the <code>.__getitem__()</code> special method. It takes whatever you placed within the square brackets and passes it as an argument to <code>.__getitem__()</code>. Therefore, <code>table["Mark"]</code> returns whatever the call to <code>PointsTable.__getitem__(table, "Mark")</code> returns:</p><pre><code>17</code></pre><p>The code outputs Mark&#8217;s points. The <code>PointsTable</code> class is now subscriptable since it has a <code>.__getitem__()</code> special method. The term <em>item</em> generally refers to the objects contained within a data structure. Therefore, <code>__getitem__()</code> is there to let you <em>get an item</em> from within a data structure.</p><p>Let&#8217;s make this example a bit more interesting before we move on to the other special methods with &#8220;get&#8221; in their name.</p><p>Try the following:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tkur!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf45cd02-ccb8-49cd-b722-045b4f629ed7_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!tkur!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf45cd02-ccb8-49cd-b722-045b4f629ed7_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!tkur!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf45cd02-ccb8-49cd-b722-045b4f629ed7_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!tkur!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf45cd02-ccb8-49cd-b722-045b4f629ed7_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!tkur!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf45cd02-ccb8-49cd-b722-045b4f629ed7_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bf45cd02-ccb8-49cd-b722-045b4f629ed7_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12188,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf45cd02-ccb8-49cd-b722-045b4f629ed7_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tkur!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf45cd02-ccb8-49cd-b722-045b4f629ed7_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!tkur!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf45cd02-ccb8-49cd-b722-045b4f629ed7_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!tkur!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf45cd02-ccb8-49cd-b722-045b4f629ed7_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!tkur!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf45cd02-ccb8-49cd-b722-045b4f629ed7_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#4</figcaption></figure></div><p>This raises an error:</p><pre><code>Traceback (most recent call last):
  File ..., line 18, in &lt;module&gt;
    print(table[&#8221;Mark&#8221;, &#8220;Stephen&#8221;])
          ~~~~~^^^^^^^^^^^^^^^^^^^
  File ..., line 6, in __getitem__
    return self._data[item]
           ~~~~~~~~~~^^^^^^
KeyError: (&#8217;Mark&#8217;, &#8216;Stephen&#8217;)</code></pre><p>There&#8217;s no key equal to <code>("Mark", "Stephen")</code>. So, you get a <code>KeyError</code>. Note how Python places parentheses around the two names when showing you the <code>KeyError</code>, even though you didn&#8217;t use the parentheses within the square brackets in <code>table["Mark", "Stephen"]</code>. This gives you a clue to what&#8217;s happening. But let&#8217;s explore this so we&#8217;re sure and we don&#8217;t just rely on our hunches:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eCzw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F976f6780-c9c5-4ba1-90fd-53ae281eae8d_1200x714.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eCzw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F976f6780-c9c5-4ba1-90fd-53ae281eae8d_1200x714.png 424w, https://substackcdn.com/image/fetch/$s_!eCzw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F976f6780-c9c5-4ba1-90fd-53ae281eae8d_1200x714.png 848w, https://substackcdn.com/image/fetch/$s_!eCzw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F976f6780-c9c5-4ba1-90fd-53ae281eae8d_1200x714.png 1272w, https://substackcdn.com/image/fetch/$s_!eCzw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F976f6780-c9c5-4ba1-90fd-53ae281eae8d_1200x714.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!eCzw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F976f6780-c9c5-4ba1-90fd-53ae281eae8d_1200x714.png" width="1200" height="714" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/976f6780-c9c5-4ba1-90fd-53ae281eae8d_1200x714.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:714,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:61008,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F976f6780-c9c5-4ba1-90fd-53ae281eae8d_1200x714.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eCzw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F976f6780-c9c5-4ba1-90fd-53ae281eae8d_1200x714.png 424w, https://substackcdn.com/image/fetch/$s_!eCzw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F976f6780-c9c5-4ba1-90fd-53ae281eae8d_1200x714.png 848w, https://substackcdn.com/image/fetch/$s_!eCzw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F976f6780-c9c5-4ba1-90fd-53ae281eae8d_1200x714.png 1272w, https://substackcdn.com/image/fetch/$s_!eCzw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F976f6780-c9c5-4ba1-90fd-53ae281eae8d_1200x714.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#5</figcaption></figure></div><p>You add two <code>print()</code> calls in <code>.__getitem__()</code>. Here&#8217;s what they print out before Python raises the <code>KeyError</code>:</p><pre><code>item=(&#8217;Mark&#8217;, &#8216;Stephen&#8217;)
type(item)=&lt;class &#8216;tuple&#8217;&gt;</code></pre><p>The object Python passes to <code>.__getitem__()</code> is the tuple <code>("Mark", "Stephen")</code>. You decide to make your <code>PointsTable</code> class super-flexible so you can include more than one name within the square brackets notation:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GN9V!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3f5248c-f575-409f-9317-f1d096d3d981_1200x798.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GN9V!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3f5248c-f575-409f-9317-f1d096d3d981_1200x798.png 424w, https://substackcdn.com/image/fetch/$s_!GN9V!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3f5248c-f575-409f-9317-f1d096d3d981_1200x798.png 848w, https://substackcdn.com/image/fetch/$s_!GN9V!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3f5248c-f575-409f-9317-f1d096d3d981_1200x798.png 1272w, https://substackcdn.com/image/fetch/$s_!GN9V!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3f5248c-f575-409f-9317-f1d096d3d981_1200x798.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!GN9V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3f5248c-f575-409f-9317-f1d096d3d981_1200x798.png" width="1200" height="798" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d3f5248c-f575-409f-9317-f1d096d3d981_1200x798.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:798,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:80562,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3f5248c-f575-409f-9317-f1d096d3d981_1200x798.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GN9V!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3f5248c-f575-409f-9317-f1d096d3d981_1200x798.png 424w, https://substackcdn.com/image/fetch/$s_!GN9V!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3f5248c-f575-409f-9317-f1d096d3d981_1200x798.png 848w, https://substackcdn.com/image/fetch/$s_!GN9V!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3f5248c-f575-409f-9317-f1d096d3d981_1200x798.png 1272w, https://substackcdn.com/image/fetch/$s_!GN9V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3f5248c-f575-409f-9317-f1d096d3d981_1200x798.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#6</figcaption></figure></div><p>The <code>.__getitem__()</code> method now checks whether <code>item</code> is a tuple first. If it is, then <code>.__getitem__()</code> returns a tuple with all the values. If <code>item</code> is not a tuple, then it must be just a single key, and the square brackets behave in the normal way:</p><pre><code>(17, 20)
(22, 20, 19)
17</code></pre><p>But note that you can no longer use an actual tuple as a dictionary key. This is fine in this example since all the keys are strings with people&#8217;s names.</p><p>So, <code>.__getitem__()</code> gives you full control over what happens when you use the square brackets notation to fetch an item from an object. Now, let&#8217;s move on to the other &#8220;get*&#8221; special methods.</p><div><hr></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://buy.stripe.com/00g3de2iGdgg4gg7su&quot;,&quot;text&quot;:&quot;Support The Python Coding Stack&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://buy.stripe.com/00g3de2iGdgg4gg7su"><span>Support The Python Coding Stack</span></a></p><div><hr></div><h2><strong>Accessing Data Using The Dot Notation, </strong><code>.</code></h2><p>You decide you also want <code>PointsTable</code> to work with the dot notation, so that you can use <code>table.Mark</code> to get the number of points Mark has.</p><p>Note that I&#8217;m adding more features to this class to demonstrate various special methods in this article. I&#8217;m not suggesting it&#8217;s necessarily always desirable to try to be too fancy with your classes!</p><p>Let&#8217;s try it out directly first, without making any changes to the class:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nohu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc743712-9d55-4eac-9780-240c7c76a2db_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!nohu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc743712-9d55-4eac-9780-240c7c76a2db_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!nohu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc743712-9d55-4eac-9780-240c7c76a2db_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!nohu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc743712-9d55-4eac-9780-240c7c76a2db_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!nohu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc743712-9d55-4eac-9780-240c7c76a2db_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cc743712-9d55-4eac-9780-240c7c76a2db_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8794,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc743712-9d55-4eac-9780-240c7c76a2db_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nohu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc743712-9d55-4eac-9780-240c7c76a2db_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!nohu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc743712-9d55-4eac-9780-240c7c76a2db_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!nohu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc743712-9d55-4eac-9780-240c7c76a2db_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!nohu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc743712-9d55-4eac-9780-240c7c76a2db_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#7</figcaption></figure></div><p>Unfortunately, <code>table.Mark</code> raises an error:</p><pre><code>Traceback (most recent call last):
  File ..., line 32, in &lt;module&gt;
    print(table.Mark)
          ^^^^^^^^^^
AttributeError: &#8216;PointsTable&#8217; object has no attribute &#8216;Mark&#8217;</code></pre><p>Python is looking for an attribute called <code>Mark</code>. Attributes are the things you can access using the dot notation in Python. Typically, these are data attributes, methods, properties, and other things you can access using the dot. However, as with everything else, Python provides special methods to handle this. But this is where it gets a bit complicated.</p><h3><code>.__getattr__()</code></h3><p>Let&#8217;s take this one step at a time. Let&#8217;s add the <code>.__getattr__()</code> special method. You can guess that this special method name stands for <em>get attribute</em> (but it&#8217;s not the only special method that stands for <em>get attribute</em>!) Let&#8217;s add this method:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tgaB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd594fd71-9cb2-4a19-b1ec-a5cf54680da4_1200x1176.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tgaB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd594fd71-9cb2-4a19-b1ec-a5cf54680da4_1200x1176.png 424w, https://substackcdn.com/image/fetch/$s_!tgaB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd594fd71-9cb2-4a19-b1ec-a5cf54680da4_1200x1176.png 848w, https://substackcdn.com/image/fetch/$s_!tgaB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd594fd71-9cb2-4a19-b1ec-a5cf54680da4_1200x1176.png 1272w, https://substackcdn.com/image/fetch/$s_!tgaB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd594fd71-9cb2-4a19-b1ec-a5cf54680da4_1200x1176.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!tgaB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd594fd71-9cb2-4a19-b1ec-a5cf54680da4_1200x1176.png" width="1200" height="1176" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d594fd71-9cb2-4a19-b1ec-a5cf54680da4_1200x1176.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1176,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:124295,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd594fd71-9cb2-4a19-b1ec-a5cf54680da4_1200x1176.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tgaB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd594fd71-9cb2-4a19-b1ec-a5cf54680da4_1200x1176.png 424w, https://substackcdn.com/image/fetch/$s_!tgaB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd594fd71-9cb2-4a19-b1ec-a5cf54680da4_1200x1176.png 848w, https://substackcdn.com/image/fetch/$s_!tgaB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd594fd71-9cb2-4a19-b1ec-a5cf54680da4_1200x1176.png 1272w, https://substackcdn.com/image/fetch/$s_!tgaB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd594fd71-9cb2-4a19-b1ec-a5cf54680da4_1200x1176.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#8</figcaption></figure></div><p>The <code>.__getattr__()</code> special method has a <code>print()</code> call to show when it&#8217;s called. This line is not required, but it will help you figure out when Python calls this special method. It&#8217;s a way of peeking into Python&#8217;s inner workings! For completeness, I added a similar line to <code>.__getitem__()</code> as well.</p><p>There are two <code>print()</code> calls at the bottom: one uses the square brackets notation, which you dealt with in the previous section, and the other uses the dot notation. Now, both work:</p><pre><code>Calling __getitem__ with argument item=&#8217;Mark&#8217;
17
Calling __getattr__ with argument name=&#8217;Mark&#8217;
17</code></pre><p>As you&#8217;ve seen in the previous section, the square brackets notation relies on <code>.__getitem__()</code>.</p><p>And as the third and fourth lines in this printout show, Python used <code>.__getattr__()</code> to deal with <code>table.Mark</code>.</p><p>Python passed the attribute name to the <code>.__getattr__()</code> special method&#8217;s <code>name</code> parameter. This method then fetches the value associated with this name from the dictionary <code>._data</code>.</p><p>This trick only works for keys that are valid attribute names and that don&#8217;t clash with real attributes or methods. That&#8217;s one reason dictionary-style access is often preferable for arbitrary user data. But let&#8217;s stick with this exercise in this article.</p><p>Also, you wrote the code such that if <code>self._data[name]</code> raises a <code>KeyError</code>, then <code>.__getattr__()</code> raises an <code>AttributeError</code>, which is what you&#8217;d expect if you try to use a name that&#8217;s not an attribute after the dot:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UFQW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe07165cb-89fe-48a3-9200-e21d9bc291ae_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!UFQW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe07165cb-89fe-48a3-9200-e21d9bc291ae_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!UFQW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe07165cb-89fe-48a3-9200-e21d9bc291ae_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!UFQW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe07165cb-89fe-48a3-9200-e21d9bc291ae_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!UFQW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe07165cb-89fe-48a3-9200-e21d9bc291ae_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e07165cb-89fe-48a3-9200-e21d9bc291ae_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8922,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe07165cb-89fe-48a3-9200-e21d9bc291ae_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UFQW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe07165cb-89fe-48a3-9200-e21d9bc291ae_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!UFQW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe07165cb-89fe-48a3-9200-e21d9bc291ae_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!UFQW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe07165cb-89fe-48a3-9200-e21d9bc291ae_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!UFQW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe07165cb-89fe-48a3-9200-e21d9bc291ae_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#9</figcaption></figure></div><p>This raises an <code>AttributeError</code>:</p><pre><code>AttributeError: &#8216;PointsTable&#8217; object has no attribute &#8216;Matilda&#8217;</code></pre><p>It seems that <code>.__getattr__()</code> is analogous to <code>.__getitem__()</code> &#8211; the first deals with <code>.</code> and the second with <code>[]</code>. But...</p><p>But things are a bit more complex. What if you try to access a standard attribute, say a data attribute or a method? You haven&#8217;t defined any methods in this class, but you do have a data attribute, <code>._data</code>. It&#8217;s marked with a leading underscore, showing you shouldn&#8217;t access it directly. But you can &#8220;break the rules&#8221;. Python won&#8217;t stop you:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hBj-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2dc4d9-5285-4e91-aba6-09f5d5b915f0_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!hBj-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2dc4d9-5285-4e91-aba6-09f5d5b915f0_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!hBj-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2dc4d9-5285-4e91-aba6-09f5d5b915f0_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!hBj-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2dc4d9-5285-4e91-aba6-09f5d5b915f0_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!hBj-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2dc4d9-5285-4e91-aba6-09f5d5b915f0_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ae2dc4d9-5285-4e91-aba6-09f5d5b915f0_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8126,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2dc4d9-5285-4e91-aba6-09f5d5b915f0_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hBj-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2dc4d9-5285-4e91-aba6-09f5d5b915f0_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!hBj-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2dc4d9-5285-4e91-aba6-09f5d5b915f0_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!hBj-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2dc4d9-5285-4e91-aba6-09f5d5b915f0_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!hBj-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2dc4d9-5285-4e91-aba6-09f5d5b915f0_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#10</figcaption></figure></div><p>The output is the following:</p><pre><code>{&#8217;Stephen&#8217;: 20, &#8216;Mark&#8217;: 17, &#8216;Kate&#8217;: 19, &#8216;Sarah&#8217;: 22}</code></pre><p>Perhaps, this is not surprising. But note that there&#8217;s no printout that says that <code>.__getattr__()</code> was called, as you got when you used <code>table.Mark</code>. Let&#8217;s look at both outputs together:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7mTC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b2ee66-3944-473f-a6b1-579871c9536b_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!7mTC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b2ee66-3944-473f-a6b1-579871c9536b_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!7mTC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b2ee66-3944-473f-a6b1-579871c9536b_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!7mTC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b2ee66-3944-473f-a6b1-579871c9536b_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!7mTC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b2ee66-3944-473f-a6b1-579871c9536b_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/27b2ee66-3944-473f-a6b1-579871c9536b_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:13926,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b2ee66-3944-473f-a6b1-579871c9536b_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7mTC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b2ee66-3944-473f-a6b1-579871c9536b_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!7mTC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b2ee66-3944-473f-a6b1-579871c9536b_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!7mTC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b2ee66-3944-473f-a6b1-579871c9536b_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!7mTC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b2ee66-3944-473f-a6b1-579871c9536b_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#11</figcaption></figure></div><p>The output now is the following:</p><pre><code>Calling __getattr__ with argument name=&#8217;Mark&#8217;
17
{&#8217;Stephen&#8217;: 20, &#8216;Mark&#8217;: 17, &#8216;Kate&#8217;: 19, &#8216;Sarah&#8217;: 22}</code></pre><p>Python called the <code>.__getattr__()</code> special method when you accessed <code>.Mark</code>, but not when you accessed <code>._data</code>.</p><p>This means there&#8217;s something else happening behind the scenes.</p><h3><code>.__getattribute__()</code></h3><p>This is where <code>.__getattribute__()</code> enters the scene. No prizes for guessing that this method name stands for <em>get attribute</em>, but then so did <code>.__getattr__()</code>. This is confusing.</p><p>And here&#8217;s the thing. It&#8217;s <code>.__getattribute__()</code> that gets called each and every time you use the dot notation with an instance. Let&#8217;s define it:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DwOO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd84a1462-4308-4235-adc5-6dea98293219_1236x924.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DwOO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd84a1462-4308-4235-adc5-6dea98293219_1236x924.png 424w, https://substackcdn.com/image/fetch/$s_!DwOO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd84a1462-4308-4235-adc5-6dea98293219_1236x924.png 848w, https://substackcdn.com/image/fetch/$s_!DwOO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd84a1462-4308-4235-adc5-6dea98293219_1236x924.png 1272w, https://substackcdn.com/image/fetch/$s_!DwOO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd84a1462-4308-4235-adc5-6dea98293219_1236x924.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!DwOO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd84a1462-4308-4235-adc5-6dea98293219_1236x924.png" width="1236" height="924" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d84a1462-4308-4235-adc5-6dea98293219_1236x924.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:924,&quot;width&quot;:1236,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:75526,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd84a1462-4308-4235-adc5-6dea98293219_1236x924.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!DwOO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd84a1462-4308-4235-adc5-6dea98293219_1236x924.png 424w, https://substackcdn.com/image/fetch/$s_!DwOO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd84a1462-4308-4235-adc5-6dea98293219_1236x924.png 848w, https://substackcdn.com/image/fetch/$s_!DwOO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd84a1462-4308-4235-adc5-6dea98293219_1236x924.png 1272w, https://substackcdn.com/image/fetch/$s_!DwOO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd84a1462-4308-4235-adc5-6dea98293219_1236x924.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#12</figcaption></figure></div><p>Note that this code breaks lots of things as it is now. We&#8217;ll fix it soon. Let&#8217;s first look at the output from this code:</p><pre><code>Calling __getattribute__ with argument name=&#8217;Mark&#8217;
None
Calling __getattribute__ with argument name=&#8217;_data&#8217;
None</code></pre><p>As you can see, Python calls <code>.__getattribute__()</code> for both <code>table.Mark</code> and <code>table._data</code>. However, the current <code>.__getattribute__()</code> method does nothing else. Therefore, it returns <code>None</code>. You&#8217;ll see soon that this is a bigger problem than you might think.</p><p>Let&#8217;s try using the dot notation to access an attribute that doesn&#8217;t exist:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ORHL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58038f2d-a8f0-4b1c-81ac-1fc5067d4b35_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!ORHL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58038f2d-a8f0-4b1c-81ac-1fc5067d4b35_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!ORHL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58038f2d-a8f0-4b1c-81ac-1fc5067d4b35_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!ORHL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58038f2d-a8f0-4b1c-81ac-1fc5067d4b35_1200x294.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ORHL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58038f2d-a8f0-4b1c-81ac-1fc5067d4b35_1200x294.png" width="1200" height="294" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/58038f2d-a8f0-4b1c-81ac-1fc5067d4b35_1200x294.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:294,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19171,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58038f2d-a8f0-4b1c-81ac-1fc5067d4b35_1200x294.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ORHL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58038f2d-a8f0-4b1c-81ac-1fc5067d4b35_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!ORHL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58038f2d-a8f0-4b1c-81ac-1fc5067d4b35_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!ORHL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58038f2d-a8f0-4b1c-81ac-1fc5067d4b35_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!ORHL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58038f2d-a8f0-4b1c-81ac-1fc5067d4b35_1200x294.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#13</figcaption></figure></div><p>Every time you use the dot, <code>.</code>, Python calls <code>.__getattribute__()</code>, which, for now, does nothing except print out the self-serving statement to show it was called:</p><pre><code>Calling __getattribute__ with argument name=&#8217;Mark&#8217;
None
Calling __getattribute__ with argument name=&#8217;_data&#8217;
None
Calling __getattribute__ with argument name=&#8217;Janine&#8217;
None</code></pre><p>Therefore, you can&#8217;t access any attribute at the moment. All data attributes and methods are out of reach. You should be careful when overriding <code>.__getattribute__()</code>. In fact, you&#8217;ll rarely need to write your own <code>.__getattribute__()</code>.</p><p>Let&#8217;s fix this code by calling <code>object.__getattribute__()</code> from within <code>PointsTable.__getattribute__()</code>. Recall that all Python classes inherit from <code>object</code>. And <code>object.__getattribute__()</code> is where the important logic that Python uses to decide what to do when you use the <code>.</code> lives. You can access <code>object</code> using <code>super()</code>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-ER0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d686747-479b-4efa-8c4c-3b77b22bad98_1236x924.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-ER0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d686747-479b-4efa-8c4c-3b77b22bad98_1236x924.png 424w, https://substackcdn.com/image/fetch/$s_!-ER0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d686747-479b-4efa-8c4c-3b77b22bad98_1236x924.png 848w, https://substackcdn.com/image/fetch/$s_!-ER0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d686747-479b-4efa-8c4c-3b77b22bad98_1236x924.png 1272w, https://substackcdn.com/image/fetch/$s_!-ER0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d686747-479b-4efa-8c4c-3b77b22bad98_1236x924.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!-ER0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d686747-479b-4efa-8c4c-3b77b22bad98_1236x924.png" width="1236" height="924" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9d686747-479b-4efa-8c4c-3b77b22bad98_1236x924.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:924,&quot;width&quot;:1236,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:77908,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d686747-479b-4efa-8c4c-3b77b22bad98_1236x924.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-ER0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d686747-479b-4efa-8c4c-3b77b22bad98_1236x924.png 424w, https://substackcdn.com/image/fetch/$s_!-ER0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d686747-479b-4efa-8c4c-3b77b22bad98_1236x924.png 848w, https://substackcdn.com/image/fetch/$s_!-ER0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d686747-479b-4efa-8c4c-3b77b22bad98_1236x924.png 1272w, https://substackcdn.com/image/fetch/$s_!-ER0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d686747-479b-4efa-8c4c-3b77b22bad98_1236x924.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#14</figcaption></figure></div><p>Note that in this class, <code>.__getattribute__()</code> is not doing anything different to the default version except for printing out a statement. I&#8217;m using this for demonstration purposes only.</p><p>Let&#8217;s deal with one <code>print()</code> call at a time. The code above includes <code>print(table._data)</code>. You&#8217;re fetching the value of a data attribute:</p><pre><code>Calling __getattribute__ with argument name=&#8217;_data&#8217;
{&#8217;Stephen&#8217;: 20, &#8216;Mark&#8217;: 17, &#8216;Kate&#8217;: 19, &#8216;Sarah&#8217;: 22}</code></pre><p>Python calls <code>PointsTable.__getattribute__()</code>, which in turn calls the base class&#8217;s <code>object.__getattribute__()</code>. This special method recognises that <code>._data</code> is an instance variable and fetches its value from the object&#8217;s <code>.__dict__</code>.</p><p>But let&#8217;s see what happens with the <code>.Mark</code> attribute access. Recall that <code>.Mark</code> is not a data attribute or method:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YSbV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7369d87f-c3ac-4252-85b8-665a45655cf0_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!YSbV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7369d87f-c3ac-4252-85b8-665a45655cf0_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!YSbV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7369d87f-c3ac-4252-85b8-665a45655cf0_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!YSbV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7369d87f-c3ac-4252-85b8-665a45655cf0_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!YSbV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7369d87f-c3ac-4252-85b8-665a45655cf0_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7369d87f-c3ac-4252-85b8-665a45655cf0_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8794,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7369d87f-c3ac-4252-85b8-665a45655cf0_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!YSbV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7369d87f-c3ac-4252-85b8-665a45655cf0_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!YSbV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7369d87f-c3ac-4252-85b8-665a45655cf0_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!YSbV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7369d87f-c3ac-4252-85b8-665a45655cf0_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!YSbV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7369d87f-c3ac-4252-85b8-665a45655cf0_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#15</figcaption></figure></div><p>Here&#8217;s the output now. Look at the various printouts, too:</p><pre><code>Calling __getattribute__ with argument name=&#8217;Mark&#8217;
Calling __getattr__ with argument name=&#8217;Mark&#8217;
Calling __getattribute__ with argument name=&#8217;_data&#8217;
17</code></pre><p>When Python sees <code>table.Mark</code>, it calls <code>PointsTable.__getattribute__(table, "Mark")</code>. This call gives rise to the first line printed out above. The base class&#8217;s <code>.__getattribute__()</code> doesn&#8217;t recognise <code>"Mark"</code> as a data attribute, a method name, or anything else it&#8217;s expecting (such as descriptors, which we will discuss soon). So, <code>object.__getattribute__()</code> actually raises an <code>AttributeError</code> in this case, which you can&#8217;t see in the output above.</p><p>That&#8217;s because when <code>.__getattribute__()</code> raises an <code>AttributeError</code> during normal attribute access using the dot, Python checks whether there&#8217;s a <code>.__getattr__()</code> defined. If there is, it uses it as a fallback. That&#8217;s what happens in this case. You can see that <code>.__getattr__()</code> is called next &#8211; that&#8217;s the second line printed out above.</p><p>But <code>.__getattr__()</code> contains this line: <code>return self._data[name]</code>. There&#8217;s a dot in that line, too. So Python needs to access <code>.__getattribute__()</code> again, this time using <code>"_data"</code> as the argument. Since <code>._data</code> is a data attribute, <code>object.__getattribute__()</code> doesn&#8217;t need to use <code>.__getattr__()</code> as a fallback since it knows how to deal with data attributes.</p><p>Confused? You&#8217;re not alone. This is confusing, I know. And there&#8217;s a bit more. But we&#8217;ll make sure things are clear by the end of this article.</p><p>But first, let&#8217;s focus on <code>table.Mark</code> again and let&#8217;s comment out, just for now, the definition of <code>.__getattr__()</code>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MknK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfef1d-4c6b-43a6-b630-400bd8b8c16b_1236x1218.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MknK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfef1d-4c6b-43a6-b630-400bd8b8c16b_1236x1218.png 424w, https://substackcdn.com/image/fetch/$s_!MknK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfef1d-4c6b-43a6-b630-400bd8b8c16b_1236x1218.png 848w, https://substackcdn.com/image/fetch/$s_!MknK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfef1d-4c6b-43a6-b630-400bd8b8c16b_1236x1218.png 1272w, https://substackcdn.com/image/fetch/$s_!MknK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfef1d-4c6b-43a6-b630-400bd8b8c16b_1236x1218.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!MknK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfef1d-4c6b-43a6-b630-400bd8b8c16b_1236x1218.png" width="1236" height="1218" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bedfef1d-4c6b-43a6-b630-400bd8b8c16b_1236x1218.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1218,&quot;width&quot;:1236,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:120637,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfef1d-4c6b-43a6-b630-400bd8b8c16b_1236x1218.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MknK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfef1d-4c6b-43a6-b630-400bd8b8c16b_1236x1218.png 424w, https://substackcdn.com/image/fetch/$s_!MknK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfef1d-4c6b-43a6-b630-400bd8b8c16b_1236x1218.png 848w, https://substackcdn.com/image/fetch/$s_!MknK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfef1d-4c6b-43a6-b630-400bd8b8c16b_1236x1218.png 1272w, https://substackcdn.com/image/fetch/$s_!MknK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfef1d-4c6b-43a6-b630-400bd8b8c16b_1236x1218.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#16</figcaption></figure></div><p>The base class&#8217;s <code>.__getattribute__()</code> method works hard to determine whether <code>"Mark"</code> is an attribute it expects. It doesn&#8217;t find it, and now, there&#8217;s no fallback <code>.__getattr__()</code>, so Python raises an error:</p><pre><code>Calling __getattribute__ with argument name=&#8217;Mark&#8217;
Calling __getattribute__ with argument name=&#8217;__dict__&#8217;
Calling __getattribute__ with argument name=&#8217;__class__&#8217;
Traceback (most recent call last):
  ...
AttributeError: &#8216;PointsTable&#8217; object has no attribute &#8216;Mark&#8217;</code></pre><p>You can see a couple of extra printouts, too. These are side effects of Python preparing the error message since we get the printout each time Python uses the dot notation, even when it does so behind the scenes. Let&#8217;s not go too far down the rabbit hole in this article. We may never come out again if we go too deep!</p><p>You&#8217;ve seen that when you use the dot notation, Python calls <code>object.__getattribute__()</code> eventually. That&#8217;s why you should include <code>super().__getattribute__()</code> when you override this special method, unless you have a clear (niche) reason why you don&#8217;t want to do this. This special method looks in a number of places for the attribute. I&#8217;m going to skip a few steps in the hierarchy for now. But we&#8217;ll revisit these in the next section (remember, we&#8217;re not done yet since there&#8217;s still <code>.__get__()</code> to deal with).</p><p>The <code>.__getattribute__()</code> special method looks for instance attributes. Let&#8217;s temporarily turn <code>.Mark</code> into an instance attribute:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!coz6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fa5c82-d0cd-4a53-8d76-cc9c486429ed_1236x966.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!coz6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fa5c82-d0cd-4a53-8d76-cc9c486429ed_1236x966.png 424w, https://substackcdn.com/image/fetch/$s_!coz6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fa5c82-d0cd-4a53-8d76-cc9c486429ed_1236x966.png 848w, https://substackcdn.com/image/fetch/$s_!coz6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fa5c82-d0cd-4a53-8d76-cc9c486429ed_1236x966.png 1272w, https://substackcdn.com/image/fetch/$s_!coz6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fa5c82-d0cd-4a53-8d76-cc9c486429ed_1236x966.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!coz6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fa5c82-d0cd-4a53-8d76-cc9c486429ed_1236x966.png" width="1236" height="966" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/06fa5c82-d0cd-4a53-8d76-cc9c486429ed_1236x966.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:966,&quot;width&quot;:1236,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:86426,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fa5c82-d0cd-4a53-8d76-cc9c486429ed_1236x966.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!coz6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fa5c82-d0cd-4a53-8d76-cc9c486429ed_1236x966.png 424w, https://substackcdn.com/image/fetch/$s_!coz6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fa5c82-d0cd-4a53-8d76-cc9c486429ed_1236x966.png 848w, https://substackcdn.com/image/fetch/$s_!coz6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fa5c82-d0cd-4a53-8d76-cc9c486429ed_1236x966.png 1272w, https://substackcdn.com/image/fetch/$s_!coz6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06fa5c82-d0cd-4a53-8d76-cc9c486429ed_1236x966.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#17</figcaption></figure></div><p>Now, <code>.Mark</code> is a data attribute, so <code>.__getattribute__()</code> finds it:</p><pre><code>Calling __getattribute__ with argument name=&#8217;Mark&#8217;
This is Mark as a data attribute</code></pre><p>If it&#8217;s not an instance variable, <code>object.__getattribute__()</code> also checks whether it&#8217;s a class attribute. Let&#8217;s test this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lD3k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92086ec8-a13c-470b-92aa-a9c986526ea9_1236x1050.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lD3k!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92086ec8-a13c-470b-92aa-a9c986526ea9_1236x1050.png 424w, https://substackcdn.com/image/fetch/$s_!lD3k!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92086ec8-a13c-470b-92aa-a9c986526ea9_1236x1050.png 848w, https://substackcdn.com/image/fetch/$s_!lD3k!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92086ec8-a13c-470b-92aa-a9c986526ea9_1236x1050.png 1272w, https://substackcdn.com/image/fetch/$s_!lD3k!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92086ec8-a13c-470b-92aa-a9c986526ea9_1236x1050.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!lD3k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92086ec8-a13c-470b-92aa-a9c986526ea9_1236x1050.png" width="1236" height="1050" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/92086ec8-a13c-470b-92aa-a9c986526ea9_1236x1050.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1050,&quot;width&quot;:1236,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:92656,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92086ec8-a13c-470b-92aa-a9c986526ea9_1236x1050.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lD3k!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92086ec8-a13c-470b-92aa-a9c986526ea9_1236x1050.png 424w, https://substackcdn.com/image/fetch/$s_!lD3k!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92086ec8-a13c-470b-92aa-a9c986526ea9_1236x1050.png 848w, https://substackcdn.com/image/fetch/$s_!lD3k!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92086ec8-a13c-470b-92aa-a9c986526ea9_1236x1050.png 1272w, https://substackcdn.com/image/fetch/$s_!lD3k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92086ec8-a13c-470b-92aa-a9c986526ea9_1236x1050.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#18</figcaption></figure></div><p>Here&#8217;s the output:</p><pre><code>Calling __getattribute__ with argument name=&#8217;Mark&#8217;
This is Mark as a class attribute</code></pre><p>Note that the line in <code>.__init__()</code> that defines <code>.Mark</code> as a data attribute is commented out now because instance attributes take precedence over class attributes. Only when all else fails (including the steps I skipped for now), does Python check whether the fallback special method <code>.__getattr__()</code> is there.</p><p>So, when you use the dot notation for attribute access (the simplified version, for now):</p><ol><li><p>Python calls <code>.__getattribute__()</code></p></li><li><p>It checks for known attributes, such as instance attributes and class attributes (and a bit more &#8211; coming soon)</p></li><li><p>If it doesn&#8217;t find anything, then <code>.__getattribute__()</code> raises an <code>AttributeError</code>. However, Python does one final check before letting this error through to the user: is there a <code>.__getattr__()</code>? If there is, Python calls it to see whether it contains instructions for handling this unknown attribute.</p></li></ol><p>I&#8217;m simplifying slightly here. Descriptors complicate the order, and we&#8217;ll return to them in the next section.</p><p>Note that in real code, you rarely need to define <code>.__getattribute__()</code>. You can rely on the default provided in the <code>object</code> base class in most cases. Most custom attribute behaviour should use <code>.__getattr__()</code> rather than <code>.__getattribute__()</code>. Use <code>.__getattribute__()</code> only when you need to intercept every attribute access. There is rarely a need for this, though.</p><p>Common uses for <code>.__getattr__()</code> include delegating missing attributes to another object, implementing lazy loading, or exposing dynamic attributes from structured data.</p><p>Here&#8217;s the tidied-up code in full so far:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-J5g!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb191478-cb58-48dc-a1cd-9755b1dba0de_1236x1596.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-J5g!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb191478-cb58-48dc-a1cd-9755b1dba0de_1236x1596.png 424w, https://substackcdn.com/image/fetch/$s_!-J5g!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb191478-cb58-48dc-a1cd-9755b1dba0de_1236x1596.png 848w, https://substackcdn.com/image/fetch/$s_!-J5g!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb191478-cb58-48dc-a1cd-9755b1dba0de_1236x1596.png 1272w, https://substackcdn.com/image/fetch/$s_!-J5g!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb191478-cb58-48dc-a1cd-9755b1dba0de_1236x1596.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!-J5g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb191478-cb58-48dc-a1cd-9755b1dba0de_1236x1596.png" width="1236" height="1596" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bb191478-cb58-48dc-a1cd-9755b1dba0de_1236x1596.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1596,&quot;width&quot;:1236,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:192027,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb191478-cb58-48dc-a1cd-9755b1dba0de_1236x1596.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-J5g!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb191478-cb58-48dc-a1cd-9755b1dba0de_1236x1596.png 424w, https://substackcdn.com/image/fetch/$s_!-J5g!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb191478-cb58-48dc-a1cd-9755b1dba0de_1236x1596.png 848w, https://substackcdn.com/image/fetch/$s_!-J5g!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb191478-cb58-48dc-a1cd-9755b1dba0de_1236x1596.png 1272w, https://substackcdn.com/image/fetch/$s_!-J5g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb191478-cb58-48dc-a1cd-9755b1dba0de_1236x1596.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#19</figcaption></figure></div><h3><strong>The Asymmetry Between </strong><code>.__getattr__()</code><strong> and </strong><code>.__setattr__()</code></h3><p>A short note: There&#8217;s an asymmetry between <code>.__getattr__()</code> and <code>.__setattr__()</code> despite their names following the same pattern. As you&#8217;ve seen above, <code>.__getattr__()</code> is only used as a fallback when <code>.__getattribute__()</code> doesn&#8217;t find the attribute through &#8220;normal routes&#8221;.</p><p>However, <code>.__setattr__()</code> doesn&#8217;t have a counterpart analogous to <code>.__getattribute__()</code>. Therefore, <code>.__setattr__()</code> is always used when setting the value of an attribute. Life is simpler in the &#8220;setting&#8221; world!</p><p>In this article, I&#8217;m focusing on the &#8220;getting&#8221; part of things, but some of the same logic applies to &#8220;setting&#8221;, too.</p><div class="pullquote"><p><em>All <a href="https://thepythoncodingplace.com?utm_source=the-python-coding-stack">The Python Coding Place</a> video courses are included into a single, cost-effective bundle. The courses cover beginner and intermediate level courses, and you also get access to a members-only forum.</em></p><p><a href="https://thepythoncodingplace.thinkific.com/enroll/2731141">Get All The Python Coding Place Courses in One Bundle</a></p></div><h2><strong>The Fourth Horseman: </strong><code>.__get__()</code></h2><p>I&#8217;ve had this article in my pipeline here on <em>The Python Coding Stack</em> for a very long time. But I never wrote it because I knew that dealing with <code>.__get__()</code> would be a pain and would need a lot of time and space. However, in March, I published <a href="https://www.thepythoncodingstack.com/p/python-descriptors-the-price-is-right">The Weird and Wonderful World of Descriptors in Python</a>. If you haven&#8217;t read that article yet, well, now is the time!</p><p>And therefore I can cheat in this article. I can avoid talking about <code>.__get__()</code> in detail. What follows in this section is a summary of the world of descriptors. See the full article for more details.</p><p>If a class has <code>.__get__()</code>, it&#8217;s a descriptor class. If it only has <code>.__get__()</code> and it does not define <code>.__set__()</code> or <code>.__delete__()</code>, which are the other methods that make up the descriptor protocol, then the class creates <em>non-data descriptors</em>. Classes that include <code>.__set__()</code> or <code>.__delete__()</code> create <em>data descriptors</em>. This distinction matters when we get back to <code>.__getattribute__()</code> and the order it uses to look for known attributes.</p><p>Let&#8217;s explore this with a dummy example. This code defines two descriptor classes and another class to test the priority order that <code>object.__getattribute__()</code> uses for different attribute types:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UR6A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F597a1293-508e-4371-9740-e538e926372f_1362x1890.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UR6A!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F597a1293-508e-4371-9740-e538e926372f_1362x1890.png 424w, https://substackcdn.com/image/fetch/$s_!UR6A!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F597a1293-508e-4371-9740-e538e926372f_1362x1890.png 848w, https://substackcdn.com/image/fetch/$s_!UR6A!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F597a1293-508e-4371-9740-e538e926372f_1362x1890.png 1272w, https://substackcdn.com/image/fetch/$s_!UR6A!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F597a1293-508e-4371-9740-e538e926372f_1362x1890.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!UR6A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F597a1293-508e-4371-9740-e538e926372f_1362x1890.png" width="1362" height="1890" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/597a1293-508e-4371-9740-e538e926372f_1362x1890.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1890,&quot;width&quot;:1362,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:273781,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F597a1293-508e-4371-9740-e538e926372f_1362x1890.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UR6A!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F597a1293-508e-4371-9740-e538e926372f_1362x1890.png 424w, https://substackcdn.com/image/fetch/$s_!UR6A!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F597a1293-508e-4371-9740-e538e926372f_1362x1890.png 848w, https://substackcdn.com/image/fetch/$s_!UR6A!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F597a1293-508e-4371-9740-e538e926372f_1362x1890.png 1272w, https://substackcdn.com/image/fetch/$s_!UR6A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F597a1293-508e-4371-9740-e538e926372f_1362x1890.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#20</figcaption></figure></div><p><code>DataDescriptor</code> defines <code>.__get__()</code> and <code>.__set__()</code> methods, which is why it creates data descriptors. However, <code>NonDataDescriptor</code> creates non-data descriptors since the class only defines <code>.__get__()</code>.</p><p>All the special methods in all classes (except <code>.__init__()</code>) have <code>print()</code> calls so you can see when Python calls them. Before we look at the output from this code, let&#8217;s review the five attributes you use in the <code>print()</code> calls and where they appear in the class definitions:</p><ol><li><p><code>.first</code> is defined as a data descriptor at the top of the <code>TestingAttributeAccess</code> class. Recall from the article on descriptors that you initially define descriptors as class attributes within a class. You also assign a value to <code>.first</code> within the class&#8217;s <code>.__init__()</code>. More on this soon.</p></li><li><p><code>.second</code> is also defined as a descriptor at the top of the <code>TestingAttributeAccess</code> class, but it&#8217;s a non-data descriptor since the <code>NonDataDescriptor</code> class only defines <code>.__get__()</code>. You also assign a value to <code>self.second</code> within <code>.__init__()</code>. We&#8217;ll see how the class actually deals with <code>.second</code> soon, since it treats it differently from <code>.first</code>.</p></li><li><p><code>.third</code> is also defined as a non-data descriptor. However, there is no other assignment to <code>third</code> within <code>.__init__()</code>. This is the key difference between <code>.second</code> and <code>.third</code> in this code.</p></li><li><p><code>.fourth</code> is a class attribute. It&#8217;s not a descriptor and it&#8217;s not an instance attribute.</p></li><li><p><code>.fifth</code> is not defined anywhere in the class, but you still call <code>print(test.fifth)</code>.</p></li></ol><p>Let&#8217;s look at the whole output from this code and then break it down into steps later:</p><pre><code>Calling DataDescriptor.__set__ with value=&#8221;&#8217;first&#8217;: a data attribute defined in .__init__&#8221;

Printing `test.first`
Calling __getattribute__ with argument name=&#8217;first&#8217;
Calling DataDescriptor.__get__
This is the Data Descriptor

Printing `test.second`
Calling __getattribute__ with argument name=&#8217;second&#8217;
&#8216;second&#8217;: a data attribute defined in .__init__

Printing `test.third`
Calling __getattribute__ with argument name=&#8217;third&#8217;
Calling NonDataDescriptor.__get__
This is the Non-Data Descriptor

Printing `test.fourth`
Calling __getattribute__ with argument name=&#8217;fourth&#8217;
I&#8217;m just a normal class attribute!

Printing `test.fifth`
Calling __getattribute__ with argument name=&#8217;fifth&#8217;
Calling __getattr__ with argument name=&#8217;fifth&#8217;
This is the fallback value from __getattr__</code></pre><p>There&#8217;s plenty to digest there. So, let&#8217;s break it down in stages.</p><h3><strong>0. Creating an instance of </strong><code>TestingAttributeAccess</code></h3><p>I want to focus on the &#8220;getting&#8221; bit from the various <code>print()</code> calls. However, there&#8217;s this line output first, so let&#8217;s deal with it, too:</p><pre><code>Calling DataDescriptor.__set__ with value=&#8221;&#8217;first&#8217;: a data attribute defined in .__init__&#8221;</code></pre><p>This happens when you create the instance of <code>TestingAttributeAccess</code> and assign it to the variable name <code>test</code>. Why? Because <code>first</code> has already been defined as a data descriptor at the time of the class definition. Therefore, when you assign a value to <code>.first</code> during initialisation, the descriptor protocol kicks in.</p><p>When you call <code>TestingAttributeAccess()</code>, Python calls the class&#8217;s <code>.__init__()</code> and soon finds this expression: <code>self.first = ...</code></p><p>Therefore, Python calls <code>DataDescriptor.__set__()</code> to set the value of this attribute. Note that <code>DataDescriptor.__set__()</code> doesn&#8217;t really set anything in this case. Normally, something more meaningful would happen in <code>.__set__()</code>. See <a href="https://www.thepythoncodingstack.com/p/python-descriptors-the-price-is-right">The Weird and Wonderful World of Descriptors in Python</a> for more on this.</p><h3><strong>1. </strong><code>test.first</code></h3><p>The next section in the code&#8217;s output is the following:</p><pre><code>Printing `test.first`
Calling __getattribute__ with argument name=&#8217;first&#8217;
Calling DataDescriptor.__get__
This is the Data Descriptor</code></pre><p>This output is created when you write <code>print(test.first)</code>. The dot notation triggers Python to call <code>.__getattribute__()</code>. So, let&#8217;s start exploring the hierarchy of checks in <code>.__getattribute__()</code>. What does this special method look for first?</p><p><strong>The </strong><em><strong>first</strong></em><strong> thing </strong><code>.__getattribute__()</code><strong> checks is whether the attribute is a data descriptor.</strong></p><p>Since <code>.first</code> is a data descriptor, the search stops there. Python calls <code>DataDescriptor.__get__()</code> and returns whatever the <code>.__get__()</code> method returns. This is the string <code>"This is the Data Descriptor"</code> in this demo example.</p><h3><strong>2. </strong><code>test.second</code></h3><p>Let&#8217;s look at the next segment in the code&#8217;s output. This is generated when you call <code>print(test.second)</code>:</p><pre><code>Printing `test.second`
Calling __getattribute__ with argument name=&#8217;second&#8217;
&#8216;second&#8217;: a data attribute defined in .__init__</code></pre><p>You know the drill by now. The dot notation is the trigger that makes Python call <code>.__getattribute__()</code>. It checks whether the attribute is a data descriptor. But <code>.second</code> is not a data descriptor. You initially define it as a <em>non-data</em> descriptor. Since it&#8217;s definitely not a data descriptor, it&#8217;s time to move on to the second check in the hierarchy.</p><p><strong>The </strong><em><strong>second</strong></em><strong> thing </strong><code>.__getattribute__()</code><strong> checks is whether the attribute is an instance attribute.</strong></p><p>Is this attribute name in the object&#8217;s <code>.__dict__</code> dictionary? Instance attributes come second in the hierarchy. Now, you&#8217;ve seen that <code>.second</code> is not a data descriptor, which is why we moved to the second check. But is <code>test.second</code> an instance attribute or a non-data descriptor?</p><p>You can see from the printout that Python never called <code>NonDataDescriptor.__get__()</code> even though you originally defined <code>.second</code> as a <code>NonDataDescriptor</code> object. The swap occurred when you created the <code>TestingAttributeAccess</code> instance. Python calls the class&#8217;s <code>.__init__()</code>, which contains this assignment line: <code>self.second = ...</code>.</p><p>Since <code>.second</code> is not a data descriptor (it&#8217;s a non-data descriptor) and doesn&#8217;t have a <code>.__set__()</code>, the descriptor protocol doesn&#8217;t kick in here to set the value of this attribute. This is different from <code>.first</code>, which was a data descriptor and, therefore, its <code>.__set__()</code> was responsible for setting the value.</p><p>Since the <code>.second</code> non-data descriptor doesn&#8217;t have a <code>.__set__()</code>, Python does what it always does in these cases: it assigns the new object, the string <code>"'second': a data attribute defined in .__init__"</code>, to <code>test.second</code>. Therefore, <code>test.second</code> is now an instance attribute containing the string rather than the original non-data descriptor.</p><p>Although <code>test.second</code> would originally access the non-data descriptor, you created an instance attribute when you initialised the object. It&#8217;s now a data attribute, which is an instance attribute.</p><p>Note that <code>TestingAttributeAccess.second</code> is still there as a class attribute, and that&#8217;s still the non-data descriptor. But <code>test.second</code> now points to another object, the string.</p><p>Therefore, when later in the code you call <code>print(test.second)</code>, Python calls <code>.__getattribute__()</code> and starts looking through its hierarchy. It&#8217;s not a data descriptor. It is an instance attribute. Therefore, it returns its value.</p><h3><strong>3. </strong><code>test.third</code></h3><p>The next block of output is the one generated when you call <code>print(test.third)</code>. Recall that <code>.third</code> is a <code>NonDataDescriptor</code> object. So was <code>.second</code> initially. However, unlike <code>.second</code>, Python never creates an instance attribute that&#8217;s assigned to <code>test.third</code> since you don&#8217;t assign to <code>self.third</code> in <code>.__init__()</code>. Here&#8217;s the output:</p><pre><code>Printing `test.third`
Calling __getattribute__ with argument name=&#8217;third&#8217;
Calling NonDataDescriptor.__get__
This is the Non-Data Descriptor</code></pre><p>In <code>print(test.third)</code>, there&#8217;s a dot again, so Python calls <code>.__getattribute__()</code>. This method first checks whether <code>.third</code> is a data descriptor. It is not. Then it checks whether <code>.third</code> is an instance attribute. It is not. So...</p><p><strong>The </strong><em><strong>third</strong></em><strong> thing </strong><code>.__getattribute__()</code><strong> checks is whether the attribute is a non-data descriptor.</strong></p><p>Non-data descriptors come next in the hierarchy. That&#8217;s why you can see the printout saying that Python calls <code>NonDataDescriptor.__get__</code> and then prints the attribute&#8217;s value, which is the value it gets from the non-data descriptor&#8217;s <code>.__get__()</code>.</p><p>Incidentally, ordinary instance methods are functions that also implement the <code>.__get__()</code> special method. They are non-data descriptors. So, methods are found in this third check in the hierarchy of checks when you use the dot notation.</p><h3><strong>4. </strong><code>test.fourth</code></h3><p>It&#8217;s <code>test.fourth</code>&#8216;s turn. Here&#8217;s the output you get from <code>print(test.fourth)</code>:</p><pre><code>Printing `test.fourth`
Calling __getattribute__ with argument name=&#8217;fourth&#8217;
I&#8217;m just a normal class attribute!</code></pre><p>There&#8217;s a dot, so there&#8217;s a call to <code>.__getattribute__()</code> which checks whether <code>.fourth</code> is a data descriptor, an instance attribute, or a non-data descriptor, in that order. It&#8217;s none of these. Time for the fourth check.</p><p><strong>The </strong><em><strong>fourth</strong></em><strong> thing </strong><code>.__getattribute__()</code><strong> checks is whether the attribute is a class attribute.</strong></p><p>And look at that! The <code>.fourth</code> attribute is indeed a class attribute.</p><h3><strong>5. </strong><code>test.fifth</code></h3><p>And finally, it&#8217;s <code>.fifth</code>&#8216;s turn. You call <code>print(test.fifth)</code> and you get the following:</p><pre><code>Printing `test.fifth`
Calling __getattribute__ with argument name=&#8217;fifth&#8217;
Calling __getattr__ with argument name=&#8217;fifth&#8217;
This is the fallback value from __getattr__</code></pre><p>Yes, yes, there&#8217;s a dot again, so Python calls <code>.__getattribute__()</code>. No, it&#8217;s not a data descriptor. No, it&#8217;s not an instance attribute. No, it&#8217;s not a non-data descriptor. No, it&#8217;s not a class attribute. It&#8217;s nothing. The attribute <code>.fifth</code> doesn&#8217;t exist.</p><p><strong>When </strong><code>.__getattribute__()</code><strong> fails to find the attribute, Python calls </strong><code>.__getattr__()</code><strong> if this special method exists.</strong></p><p>And that&#8217;s it. Simple, eh?!?!</p><p>Let&#8217;s finish off by summarising the order in which Python deals with looking for attributes when you use the dot notation:</p><ol><li><p>The <em>first</em> thing <code>.__getattribute__()</code> checks is whether the attribute is a data descriptor.</p></li><li><p>The <em>second</em> thing <code>.__getattribute__()</code> checks is whether the attribute is an instance attribute.</p></li><li><p>The <em>third</em> thing <code>.__getattribute__()</code> checks is whether the attribute is a non-data descriptor.</p></li><li><p>The <em>fourth</em> thing <code>.__getattribute__()</code> checks is whether the attribute is a class attribute.</p></li><li><p>Finally, when <code>.__getattribute__()</code> fails to find the attribute, Python calls <code>.__getattr__()</code> if this special method exists.</p></li></ol><p>The fifth step occurs when you access attributes using the dot notation. If you call <code>.__getattribute__() </code>explicitly, which you rarely need to do, Python doesn&#8217;t automatically look for <code>.__getattr__()</code>.</p><p>Note that Python looks for descriptors, instance and class attributes in the class and its base classes, too, following the method resolution order.</p><h2><strong>Final Words</strong></h2><p>Wait for this. I&#8217;ve been waiting all article to write this:</p><blockquote><p>Now, you won&#8217;t <em>forget</em> the <em>four &#8220;get</em>*&#8221; special methods.</p></blockquote><p>Got it! (My son just walked out disapprovingly when he read this.)</p><p>You may never need to use all of these methods. Maybe you&#8217;ll never need to use any of them. But if you ever wondered why there are these four special methods with similar names, now you know what they do. And you dug a bit more underneath the Python surface along the way. And that&#8217;s always fun.</p><div class="pullquote"><p><em>Your call&#8230;</em></p><p><em>The Python Coding Place offers something for everyone:</em></p><p><em>&#8226; a super-personalised one-to-one 6-month mentoring option</em><br><em>$ 4,750</em></p><p><em>&#8226; individual one-to-one sessions</em><br><em>$ 125</em></p><p><em>&#8226; a self-led route with access to 60+ hrs of exceptional video courses and a support forum</em><br><em>$ 400</em></p><p><em><a href="https://thepythoncodingplace.com?utm_source=the-python-coding-stack">Which The Python Coding Place student are you?</a></em></p></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FUK2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F708dc396-00c0-4ca1-85f4-3a00f69ca4f5_4936x3290.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FUK2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F708dc396-00c0-4ca1-85f4-3a00f69ca4f5_4936x3290.jpeg 424w, https://substackcdn.com/image/fetch/$s_!FUK2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F708dc396-00c0-4ca1-85f4-3a00f69ca4f5_4936x3290.jpeg 848w, https://substackcdn.com/image/fetch/$s_!FUK2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F708dc396-00c0-4ca1-85f4-3a00f69ca4f5_4936x3290.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!FUK2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F708dc396-00c0-4ca1-85f4-3a00f69ca4f5_4936x3290.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!FUK2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F708dc396-00c0-4ca1-85f4-3a00f69ca4f5_4936x3290.jpeg" width="628" height="418.3791208791209" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/708dc396-00c0-4ca1-85f4-3a00f69ca4f5_4936x3290.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:970,&quot;width&quot;:1456,&quot;resizeWidth&quot;:628,&quot;bytes&quot;:1156165,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/196397351?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F708dc396-00c0-4ca1-85f4-3a00f69ca4f5_4936x3290.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FUK2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F708dc396-00c0-4ca1-85f4-3a00f69ca4f5_4936x3290.jpeg 424w, https://substackcdn.com/image/fetch/$s_!FUK2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F708dc396-00c0-4ca1-85f4-3a00f69ca4f5_4936x3290.jpeg 848w, https://substackcdn.com/image/fetch/$s_!FUK2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F708dc396-00c0-4ca1-85f4-3a00f69ca4f5_4936x3290.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!FUK2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F708dc396-00c0-4ca1-85f4-3a00f69ca4f5_4936x3290.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em><a href="https://www.pexels.com/photo/an-abstract-painting-with-orange-and-blue-waves-21243683/">Photo by Robert Clark</a></em></p><div><hr></div><p><em>Code in this article uses Python 3.14</em></p><p><em>The code images used in this article are created using <a href="https://snappify.cello.so/f4AsFrwgwov">Snappify</a>.</em> <em>[Affiliate link]</em></p><p><em><strong><a href="https://www.thepythoncodingstack.com/subscribe">Join</a></strong></em><strong><a href="https://www.thepythoncodingstack.com/subscribe"> The Club</a></strong><em>, the exclusive area for paid subscribers for <a href="https://www.thepythoncodingstack.com/s/the-club">more Python posts</a>, videos, a members&#8217; forum, and more.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><p><em>You can also support this publication by making a <a href="https://buy.stripe.com/00g3de2iGdgg4gg7su">one-off contribution of any amount you wish</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://buy.stripe.com/00g3de2iGdgg4gg7su&quot;,&quot;text&quot;:&quot;Support The Python Coding Stack&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://buy.stripe.com/00g3de2iGdgg4gg7su"><span>Support The Python Coding Stack</span></a></p><div><hr></div><p><em>For more Python resources, you can also visit</em> <em><a href="https://realpython.com?utm_source=the-python-coding-stack">Real Python</a>&#8212;you may even stumble on one of my own articles or courses there!</em></p><p><em>Also, are you interested in technical writing? You&#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at</em> <em><a href="http://stephengruppetta.com/breaking-the-rules">Breaking the Rules</a></em>.</p><p><em>And you can find out more about me at</em> <em><a href="https://stephengruppetta.com?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p><p>Further reading related to this article&#8217;s topic:</p><ul><li><p><a href="https://www.thepythoncodingstack.com/p/python-getitem-special-method-library-manorhouse">The Manor House, the Oak-Panelled Library, the Vending Machine, and Python&#8217;s </a><strong>`</strong><code>_getitem__()`</code></p></li><li><p><a href="https://www.thepythoncodingstack.com/p/python-descriptors-the-price-is-right">The Weird and Wonderful World of Descriptors in Python &#8226; The Price is Right</a></p></li></ul><div><hr></div><h2>Appendix: Code Blocks</h2><h5>Code Block #1</h5><pre><code><code>class PointsTable:
    def __init__(self, data):
        self._data = dict(data)

table = PointsTable(
    {
        "Stephen": 20,
        "Mark": 17,
        "Kate": 19,
        "Sarah": 22,
    }
)
</code></code></pre><h5>Code Block #2</h5><pre><code><code># ...
print(table["Mark"])
</code></code></pre><h5>Code Block #3</h5><pre><code><code>class PointsTable:
    def __init__(self, data):
        self._data = dict(data)

    def __getitem__(self, item):
        return self._data[item]

table = PointsTable(
    {
        "Stephen": 20,
        "Mark": 17,
        "Kate": 19,
        "Sarah": 22,
    }
)

print(table["Mark"])
</code></code></pre><h5>Code Block #4</h5><pre><code><code># ...
print(table["Mark", "Stephen"])
</code></code></pre><h5>Code Block #5</h5><pre><code><code>class PointsTable:
    def __init__(self, data):
        self._data = dict(data)

    def __getitem__(self, item):
        print(f"{item=}")
        print(f"{type(item)=}")
        return self._data[item]

table = PointsTable(
    # ...
)

print(table["Mark", "Stephen"])
</code></code></pre><h5>Code Block #6</h5><pre><code><code>class PointsTable:
    def __init__(self, data):
        self._data = dict(data)

    def __getitem__(self, item):
        if isinstance(item, tuple):
            return tuple(self._data[key] for key in item)
        return self._data[item]

table = PointsTable(
    # ...
)

print(table["Mark", "Stephen"])
print(table["Sarah", "Stephen", "Kate"])
print(table["Mark"])
</code></code></pre><h5>Code Block #7</h5><pre><code><code># ...
print(table.Mark)
</code></code></pre><h5>Code Block #8</h5><pre><code><code>class PointsTable:
    # ...

    def __getitem__(self, item):
        print(f"Calling __getitem__ with argument {item=}")
        if isinstance(item, tuple):
            return tuple(self._data[key] for key in item)
        return self._data[item]

    def __getattr__(self, name):
        print(f"Calling __getattr__ with argument {name=}")
        try:
            return self._data[name]
        except KeyError:
            raise AttributeError(
                f"'{type(self).__name__}' object has "
                f"no attribute '{name}'"
            ) from None

table = PointsTable(
    # ...
)

print(table["Mark"])
print(table.Mark)
</code></code></pre><h5>Code Block #9</h5><pre><code><code># ...
print(table.Matilda)
</code></code></pre><h5>Code Block #10</h5><pre><code><code># ...
print(table._data)
</code></code></pre><h5>Code Block #11</h5><pre><code><code># ...
print(table.Mark)
print(table._data)
</code></code></pre><h5>Code Block #12</h5><pre><code><code>class PointsTable:
    def __init__(self, data):
        self._data = dict(data)

    def __getitem__(self, item):
        # ...

    def __getattr__(self, name):
        # ...

    def __getattribute__(self, name):
        print(f"Calling __getattribute__ with argument {name=}")

table = PointsTable(
    # ...
)

print(table.Mark)
print(table._data)
</code></code></pre><h5>Code Block #13</h5><pre><code><code># ...
print(table.Mark)
print(table._data)
print(table.Janine)
</code></code></pre><h5>Code Block #14</h5><pre><code><code>class PointsTable:
    def __init__(self, data):
        self._data = dict(data)

    def __getitem__(self, item):
        # ...

    def __getattr__(self, name):
        # ...

    def __getattribute__(self, name):
        print(f"Calling __getattribute__ with argument {name=}")
        return super().__getattribute__(name)

table = PointsTable(
    # ...
)

print(table._data)
</code></code></pre><h5>Code Block #15</h5><pre><code><code># ...
print(table.Mark)
</code></code></pre><h5>Code Block #16</h5><pre><code><code>class PointsTable:
    def __init__(self, data):
        self._data = dict(data)

    def __getitem__(self, item):
        # ...

    # def __getattr__(self, name):
    #     print(f"Calling __getattr__ with argument {name=}")
    #     try:
    #         return self._data[name]
    #     except KeyError:
    #         raise AttributeError(
    #             f"'{type(self).__name__}' object has "
    #             f"no attribute '{name}'"
    #         ) from None

    def __getattribute__(self, name):
        print(f"Calling __getattribute__ with argument {name=}")
        return super().__getattribute__(name)

table = PointsTable(
    # ...
)

print(table.Mark)
</code></code></pre><h5>Code Block #17</h5><pre><code><code>class PointsTable:
    def __init__(self, data):
        self._data = dict(data)
        self.Mark = "This is Mark as a data attribute"

    def __getitem__(self, item):
        # ...

    # def __getattr__(self, name):
    # ...

    def __getattribute__(self, name):
        print(f"Calling __getattribute__ with argument {name=}")
        return super().__getattribute__(name)

table = PointsTable(
    # ...
)

print(table.Mark)
</code></code></pre><h5>Code Block #18</h5><pre><code><code>class PointsTable:
    Mark = "This is Mark as a class attribute"

    def __init__(self, data):
        self._data = dict(data)
        # self.Mark = "This is Mark as a data attribute"

    def __getitem__(self, item):
        # ...

    # def __getattr__(self, name):
    # ...

    def __getattribute__(self, name):
        print(f"Calling __getattribute__ with argument {name=}")
        return super().__getattribute__(name)

table = PointsTable(
    # ...
)

print(table.Mark)
</code></code></pre><h5>Code Block #19</h5><pre><code><code>class PointsTable:
    def __init__(self, data):
        self._data = dict(data)

    def __getitem__(self, item):
        print(f"Calling __getitem__ with argument {item=}")
        if isinstance(item, tuple):
            return tuple(self._data[key] for key in item)
        return self._data[item]

    def __getattr__(self, name):
        print(f"Calling __getattr__ with argument {name=}")
        try:
            return self._data[name]
        except KeyError:
            raise AttributeError(
                f"'{type(self).__name__}' object has "
                f"no attribute '{name}'"
            ) from None

    def __getattribute__(self, name):
        print(f"Calling __getattribute__ with argument {name=}")
        return super().__getattribute__(name)

table = PointsTable(
    {
        "Stephen": 20,
        "Mark": 17,
        "Kate": 19,
        "Sarah": 22,
    }
)

print(table._data)  # Only .__getattribute__() used
print(table.Mark)  # Fallback .__getattr__() needed here
</code></code></pre><h5>Code Block #20</h5><pre><code><code>class DataDescriptor:
    def __get__(self, instance, owner):
        print("Calling DataDescriptor.__get__")
        return "This is the Data Descriptor"

    def __set__(self, instance, value):
        print(f"Calling DataDescriptor.__set__ with {value=}")

class NonDataDescriptor:
    def __get__(self, instance, owner):
        print("Calling NonDataDescriptor.__get__")
        return "This is the Non-Data Descriptor"

class TestingAttributeAccess:
    first = DataDescriptor()
    second = NonDataDescriptor()
    third = NonDataDescriptor()
    fourth = "I'm just a normal class attribute!"

    def __init__(self):
        self.first = "'first': a data attribute defined in .__init__"
        self.second = "'second': a data attribute defined in .__init__"

    def __getattribute__(self, name):
        print(f"Calling __getattribute__ with argument {name=}")
        return super().__getattribute__(name)

    def __getattr__(self, name):
        print(f"Calling __getattr__ with argument {name=}")
        return "This is the fallback value from __getattr__"

test = TestingAttributeAccess()
print("\nPrinting `test.first`")
print(test.first)
print("\nPrinting `test.second`")
print(test.second)
print("\nPrinting `test.third`")
print(test.third)
print("\nPrinting `test.fourth`")
print(test.fourth)
print("\nPrinting `test.fifth`")
print(test.fifth)</code></code></pre><div><hr></div><p><em>For more Python resources, you can also visit</em> <em><a href="https://realpython.com?utm_source=the-python-coding-stack">Real Python</a>&#8212;you may even stumble on one of my own articles or courses there!</em></p><p><em>Also, are you interested in technical writing? You&#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at</em> <em><a href="http://stephengruppetta.com/breaking-the-rules">Breaking the Rules</a></em>.</p><p><em>And you can find out more about me at</em> <em><a href="https://stephengruppetta.com?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p>]]></content:encoded></item><item><title><![CDATA[Doubling Down on Python in The Age of AI]]></title><description><![CDATA[&#8230;and why you haven&#8217;t seen AI posts on here (yet)]]></description><link>https://www.thepythoncodingstack.com/p/doubling-down-on-python-in-the-age</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/doubling-down-on-python-in-the-age</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Fri, 24 Apr 2026 08:53:41 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!19bs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa037bf24-5372-476e-9e60-b5516e5e1f7d_4101x3361.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you&#8217;ve been wondering where I&#8217;ve been, yes, it&#8217;s been quieter than usual around here. No dramatic reason. Just life, work, the usual stuff.</p><p>But one thing kept catching my attention: all the noise outside. Everyone&#8217;s talking about AI writing code, agents shipping products, the death of programming. And every so often, someone asks me &#8212; usually with that slightly guilty look of someone who thinks they&#8217;re about to insult my livelihood &#8212; &#8220;but do you really still need to <em>learn</em> Python? In this age?&#8221;</p><p>So I&#8217;ve been thinking about it. Properly. And I wanted to share where I&#8217;ve landed, because I think the answer matters and it&#8217;s different from what some hot takes suggest.</p><h2><strong>Do I still need to learn to code?</strong></h2><p>The way we write computer programs is changing. I don&#8217;t have a crystal ball for what programming looks like in five or ten years.</p><p>But here&#8217;s the thing: <em>neither does anyone else.</em></p><p>Here&#8217;s what I know from watching others and experimenting with AI tools in my own work: right now, the people getting the most out of AI are the ones who already know how to code.</p><p>Here&#8217;s the hierarchy as I see it today:</p><ul><li><p>No coding knowledge = Little benefit from AI</p></li><li><p>Little coding knowledge = Intermediate benefit from AI</p></li><li><p>Intermediate coding knowledge = Great benefit from AI</p></li><li><p>Great coding knowledge = Superpower-like benefit from AI</p></li></ul><p>We&#8217;re in an era where some coding knowledge takes you much further than it could have a few years ago. That&#8217;s not an argument against learning to code. It&#8217;s an argument for it.</p><p>&#8220;But what about those vibe coding people? They seem to be shipping things.&#8221; Some are. I&#8217;ll be honest about that.</p><p>The projects I&#8217;ve seen from pure vibe coders tend to be smaller, tend to follow well-trodden patterns, and often hit a ceiling when something goes slightly wrong or slightly off-piste. Which is fine for a side project. I just created a useful dashboard to help me organise my day the way I want to using this approach.</p><p>But it tells you something: the AI does the heavy lifting on the known stuff. The moment something needs genuine thinking, you need the human who knows what&#8217;s going on beneath the surface.</p><h2><strong>AI-Assisted Human Coding and Human-Assisted AI Coding</strong></h2><p>Most serious work right now is a partnership.</p><p>Sometimes it&#8217;s <em>AI-assisted human coding</em>. The human drives, AI assists.</p><p>Sometimes it&#8217;s <em>human-assisted AI programming</em>. The AI writes most of the code, but the human knows what to ask for, how to steer it toward good design, how to evaluate whether the output actually makes sense.</p><p>Even when the coding looks like it&#8217;s done by AI, the person prompting and reviewing it is generally an experienced programmer. They&#8217;ve learned enough Python to know what&#8217;s reasonable, what&#8217;s a red flag, and when the AI is confidently wrong.</p><p>I&#8217;ve been experimenting with agentic AI over the past few weeks. I&#8217;ve tackled side projects I&#8217;d never have had the time for before. I didn&#8217;t have the time to start then, let alone finish them. Some of that output will show up in other places &#8212; stay tuned! But not here. <em>The Python Coding Stack</em> is the place for <em>my</em> writing.</p><h2><strong>The Programming Mindset When Talking to AI Agents</strong></h2><p>Here&#8217;s one thing I noticed. Talking to AI agents isn&#8217;t like talking to humans. But it isn&#8217;t like talking to computers (a.k.a. programming) either.</p><p>You need both qualities at the same time.</p><p>You need the clarity and communication skills you&#8217;d use with a person &#8212; explaining context, setting direction, knowing what matters, using clear language.</p><p>And you need the precision you&#8217;d use when programming &#8212; no ambiguity, clear intent, structure.</p><p><strong>A good programmer who&#8217;s also a good communicator is the best human to work with AI agents.</strong></p><p>That&#8217;s not coincidental. The same thought habits that make you an effective programmer also make you an effective prompter and reviewer when AI is involved.</p><p>I&#8217;ll share some of the prompts I&#8217;m using in a future post and analyse them to discuss why I wrote what I wrote. Learning to code well gives you an unfair advantage in this new world.</p><h2><strong>There&#8217;s Never Been a Better Time to Learn Python</strong></h2><p>Here&#8217;s what I&#8217;ve convinced myself of after all this. <em>There&#8217;s never been a better time to learn Python.</em></p><p>A few years ago, you needed to reach an intermediate-to-advanced level before you could do something genuinely useful with Python. The bar was high. Now, with AI assistance, the bar is lower. Less Python knowledge takes you further than ever.</p><p>What used to need expertise can now be explored with curiosity and a bit of intermediate-ish-level Python.</p><p>That&#8217;s not replacing deeper learning. It&#8217;s making the entry point more accessible. And once you&#8217;re in, you can go as deep as you want.</p><p>So yes, I&#8217;ll keep coding in Python. Sometimes with a bit of help from AI. Sometimes with a lot of help from AI.</p><p>And yes, I&#8217;ll keep writing about Python here, as I&#8217;ve always done.</p><h2><strong>The Fun Factor</strong></h2><p>Here&#8217;s the thing we don&#8217;t talk about enough when discussing programming. It&#8217;s fun. It&#8217;s challenging. It&#8217;s rewarding. It&#8217;s fulfilling. It&#8217;s stimulating. It keeps my brain active.</p><p>I code because I enjoy it. I&#8217;ll keep writing about Python because I enojoy that, too,, and because I find value in sharing here &#8212; for myself and, hopefully, for you too.</p><p>Normal service resumes here. More Python posts coming. And maybe, just maybe, some of the AI things I&#8217;m learning will make their way in, too.</p><p><em>Psst&#8211;did you know you can become a premium member to be a part of The Club? It would mean so much to me!</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>Quick question:</p><div class="poll-embed" data-attrs="{&quot;id&quot;:501089}" data-component-name="PollToDOM"></div><p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!19bs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa037bf24-5372-476e-9e60-b5516e5e1f7d_4101x3361.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!19bs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa037bf24-5372-476e-9e60-b5516e5e1f7d_4101x3361.jpeg 424w, https://substackcdn.com/image/fetch/$s_!19bs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa037bf24-5372-476e-9e60-b5516e5e1f7d_4101x3361.jpeg 848w, https://substackcdn.com/image/fetch/$s_!19bs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa037bf24-5372-476e-9e60-b5516e5e1f7d_4101x3361.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!19bs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa037bf24-5372-476e-9e60-b5516e5e1f7d_4101x3361.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!19bs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa037bf24-5372-476e-9e60-b5516e5e1f7d_4101x3361.jpeg" width="599" height="490.9141672762741" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a037bf24-5372-476e-9e60-b5516e5e1f7d_4101x3361.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:3361,&quot;width&quot;:4101,&quot;resizeWidth&quot;:599,&quot;bytes&quot;:3028108,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/195326850?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26c84d66-33d1-4bbc-9fa5-2e5e0f6ab8e0_4101x6151.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!19bs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa037bf24-5372-476e-9e60-b5516e5e1f7d_4101x3361.jpeg 424w, https://substackcdn.com/image/fetch/$s_!19bs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa037bf24-5372-476e-9e60-b5516e5e1f7d_4101x3361.jpeg 848w, https://substackcdn.com/image/fetch/$s_!19bs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa037bf24-5372-476e-9e60-b5516e5e1f7d_4101x3361.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!19bs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa037bf24-5372-476e-9e60-b5516e5e1f7d_4101x3361.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Photo by <a href="https://www.pexels.com/photo/a-person-holding-a-pair-of-black-framed-eyeglasses-9929033/">Maksim Goncharenok</a></em></p><div><hr></div><p><em><strong><a href="https://www.thepythoncodingstack.com/subscribe">Join</a></strong></em><strong><a href="https://www.thepythoncodingstack.com/subscribe"> The Club</a></strong><em>, the exclusive area for paid subscribers for <a href="https://www.thepythoncodingstack.com/s/the-club">more Python posts</a>, videos, a members&#8217; forum, and more.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><p><em>You can also support this publication by making a <a href="https://buy.stripe.com/00g3de2iGdgg4gg7su">one-off contribution of any amount you wish</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://buy.stripe.com/00g3de2iGdgg4gg7su&quot;,&quot;text&quot;:&quot;Support The Python Coding Stack&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://buy.stripe.com/00g3de2iGdgg4gg7su"><span>Support The Python Coding Stack</span></a></p><div><hr></div><p><em>For more Python resources, you can also visit</em> <em><a href="https://realpython.com?utm_source=the-python-coding-stack">Real Python</a>&#8212;you may even stumble on one of my own articles or courses there!</em></p><p><em>Also, are you interested in technical writing? You&#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at</em> <em><a href="http://stephengruppetta.com/breaking-the-rules">Breaking the Rules</a></em>.</p><p><em>And you can find out more about me at</em> <em><a href="https://stephengruppetta.com?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p>]]></content:encoded></item><item><title><![CDATA[3 • 7600 • 33 • 121 • When Python Stacks Up]]></title><description><![CDATA[When I was a child, I used to pace up and down the corridor at home pretending to teach an imaginary group of people.]]></description><link>https://www.thepythoncodingstack.com/p/when-python-stacks-up-3-year-anniversary</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/when-python-stacks-up-3-year-anniversary</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Tue, 24 Mar 2026 14:16:07 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!koik!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09671ebf-1c6c-4eec-bf1b-ea8b8adb06f5_1920x2880.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When I was a child, I used to pace up and down the corridor at home pretending to teach an imaginary group of people. It was my way of learning.</p><p>It still is.</p><p>I started writing about Python as a learning tool&#8212;to help me sort things out in my head, weave a thread through all the disparate bits of information, clarify my thoughts, make sure any knowledge gaps are filled.</p><p>I started <em>The Python Coding Stack</em> <strong>three</strong> years ago. That&#8217;s the first of the mystery numbers in the post&#8217;s title revealed! I had written elsewhere before, but at the time of starting <em>The Stack</em>, I felt I had found my own &#8220;Python voice&#8221;. I had been teaching Python for nearly a decade. I had written plenty of articles, but setting up <em>The Python Coding Stack</em> was a deliberate choice to step up. I was still writing articles primarily for my own benefit, but now I was also writing for others, hoping they would want to learn the way I do.</p><p>And <strong>7,600</strong> subscribers apparently do. Thank you for joining this journey, whether you were there three years ago or you joined a few days ago. If you just joined, there&#8217;s an archive of <strong>121</strong> articles, most of them long-form tutorials or step-by-step guides.</p><p>A special thank you to the <strong>33</strong> subscribers who chose to upgrade to premium and join <em>The Club</em>. It may only amount to 3 coffees per month for you, but it makes a difference to me. Thank you! I hope you&#8217;ve been enjoying the exclusive content for <em>The Club</em> members.</p><p>And perhaps, <a href="https://www.thepythoncodingstack.com/subscribe">if a few more decide to join you in </a><em><a href="https://www.thepythoncodingstack.com/subscribe">The Club</a></em> (you can surely cut three coffees out of your monthly intake!), then this publication may even become self-sustainable. Your support can make a real difference&#8212;if you value these articles and want to see them continue, please consider joining now. At the moment, I give up a lot of my time for free to think about my articles, plan them, draft them, review them technically, review them linguistically, get them ready for publication, and then publish.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>I mentioned my live teaching earlier. My written articles and my live teaching have a lot in common. One of the hardest things about teaching (or communication in general) is to place yourself in the learner&#8217;s mindset. I know, it&#8217;s obvious. But it&#8217;s hard.</p><p>A string of words can make perfect sense to someone who already understands the concept, but it&#8217;s hard to understand for someone learning it for the first time.</p><p>Going from A to B can be a smooth reasoning step for an expert, but requires a few more intermediate steps for a novice.</p><p>A trait that helps me in my teaching is my ability to recall the pain points I had when learning a topic. Everything is easy once you know it, but hard when you don&#8217;t. Remembering that what comes easily today was once hard is essential for teaching, whatever the format.</p><p>I often use my writing to help me with my live teaching. And, just as often, I discover a new angle or insight during live teaching that I then put down in writing. It&#8217;s a two-way street. Both forms of communication&#8212;live teaching and writing&#8212;complement each other.</p><p>All this to say that I enjoy writing these articles. They&#8217;re useful for me personally, and for my work teaching Python. And I hope they&#8217;re useful for you.</p><div><hr></div><p>121 articles. The clich&#233; would have me say that choosing favourites is like choosing a favourite child. But that&#8217;s not the case. There are articles I like less than others. So, I tried to put together a highlights reel of the past three years. Here we go&#8230;</p><p><em>The Centre of the Python Universe &#8226; Objects</em></p><ul><li><p><a href="https://thepythoncodingstack.substack.com/p/my-life-the-autobiography-of-a-python-object">My Life &#8226; The Autobiography of a Python Object</a></p></li><li><p><a href="https://thepythoncodingstack.substack.com/p/python-is-and-equals-equals-understanding-them-using-a-5-pound-note">The One About the &#163;5 Note and the Trip to the Coffee Shop &#8226; The Difference Between 'is' and '==' in Python</a></p></li><li><p><a href="https://thepythoncodingstack.substack.com/p/python-duck-typing-functions-classes-callables">When a Duck Calls Out &#8226; On Duck Typing and Callables in Python</a></p></li></ul><p><em>A Stroll Across Python &#8226; Fancy and Not So Fancy Tools</em></p><ul><li><p><a href="https://thepythoncodingstack.substack.com/p/python-f-strings-curious-little-shop-sign">The Curious Little Shop at The End of My Street &#8226; Python&#8217;s f-strings</a></p></li><li><p><a href="https://thepythoncodingstack.substack.com/p/do-you-really-know-how-or-and-and-work-in-python">Do You Really Know How 'or' And 'and' Work in Python?</a></p></li><li><p><a href="https://thepythoncodingstack.substack.com/p/conditional-expression-ternary-operator-list-com">If You Find if..else in List Comprehensions Confusing, Read This, Else&#8230;</a></p></li></ul><p><em>Where Do I Store This? &#8226; Data Types and Structures</em></p><ul><li><p><a href="https://thepythoncodingstack.substack.com/p/clearing-the-deque-python-linked-list">Clearing The Deque&#8212;Tidying My Daughter&#8217;s Soft Toys &#8226; A Python Picture Story</a></p></li><li><p><a href="https://thepythoncodingstack.substack.com/p/are-python-dictionaries-ordered-data">Are Python Dictionaries Ordered Data Structures?</a></p></li><li><p><a href="https://thepythoncodingstack.substack.com/p/hermiones-undetectable-extension-charm-python-objects-references-data-structures">Hermione&#8217;s Undetectable Extension Charm: Revealed (Using Python)</a></p></li><li><p><code>bytes</code><a href="https://thepythoncodingstack.substack.com/p/bytes-python-built-in-unicode-utf-8-encoding">: The Lesser-Known Python Built-In Sequence &#8226; And Understanding UTF-8 Encoding</a></p></li><li><p><a href="https://thepythoncodingstack.substack.com/p/wheres-william-python-hash-hashable">Where&#8217;s William? How Quickly Can You Find Him? &#8226; What&#8217;s a Python Hashable Object?</a></p></li></ul><p>And here are the posts in <em>The Club</em> section of this publication, exclusive for premium subscribers: <a href="https://www.thepythoncodingstack.com/s/the-club">The Club | The Python Coding Stack</a></p><div><hr></div><p>Happy 3rd Birthday to <em>The Python Coding Stack</em>. From just under a hundred people in the first week to 7,600+ today, this community has grown thanks to your enthusiasm.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OyEa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe138b8b3-38bc-4a39-88f4-85b708229a58_2849x1313.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OyEa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe138b8b3-38bc-4a39-88f4-85b708229a58_2849x1313.jpeg 424w, https://substackcdn.com/image/fetch/$s_!OyEa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe138b8b3-38bc-4a39-88f4-85b708229a58_2849x1313.jpeg 848w, https://substackcdn.com/image/fetch/$s_!OyEa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe138b8b3-38bc-4a39-88f4-85b708229a58_2849x1313.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!OyEa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe138b8b3-38bc-4a39-88f4-85b708229a58_2849x1313.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!OyEa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe138b8b3-38bc-4a39-88f4-85b708229a58_2849x1313.jpeg" width="1456" height="671" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e138b8b3-38bc-4a39-88f4-85b708229a58_2849x1313.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:671,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:164324,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191982167?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe138b8b3-38bc-4a39-88f4-85b708229a58_2849x1313.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!OyEa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe138b8b3-38bc-4a39-88f4-85b708229a58_2849x1313.jpeg 424w, https://substackcdn.com/image/fetch/$s_!OyEa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe138b8b3-38bc-4a39-88f4-85b708229a58_2849x1313.jpeg 848w, https://substackcdn.com/image/fetch/$s_!OyEa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe138b8b3-38bc-4a39-88f4-85b708229a58_2849x1313.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!OyEa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe138b8b3-38bc-4a39-88f4-85b708229a58_2849x1313.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Let&#8217;s keep up the momentum&#8212;consider joining <em>The Club</em> today! Your membership can help ensure The Python Coding Stack continues on its path, stronger than ever.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!koik!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09671ebf-1c6c-4eec-bf1b-ea8b8adb06f5_1920x2880.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!koik!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09671ebf-1c6c-4eec-bf1b-ea8b8adb06f5_1920x2880.jpeg 424w, https://substackcdn.com/image/fetch/$s_!koik!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09671ebf-1c6c-4eec-bf1b-ea8b8adb06f5_1920x2880.jpeg 848w, https://substackcdn.com/image/fetch/$s_!koik!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09671ebf-1c6c-4eec-bf1b-ea8b8adb06f5_1920x2880.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!koik!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09671ebf-1c6c-4eec-bf1b-ea8b8adb06f5_1920x2880.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!koik!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09671ebf-1c6c-4eec-bf1b-ea8b8adb06f5_1920x2880.jpeg" width="442" height="663" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/09671ebf-1c6c-4eec-bf1b-ea8b8adb06f5_1920x2880.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2184,&quot;width&quot;:1456,&quot;resizeWidth&quot;:442,&quot;bytes&quot;:323402,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191982167?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09671ebf-1c6c-4eec-bf1b-ea8b8adb06f5_1920x2880.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!koik!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09671ebf-1c6c-4eec-bf1b-ea8b8adb06f5_1920x2880.jpeg 424w, https://substackcdn.com/image/fetch/$s_!koik!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09671ebf-1c6c-4eec-bf1b-ea8b8adb06f5_1920x2880.jpeg 848w, https://substackcdn.com/image/fetch/$s_!koik!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09671ebf-1c6c-4eec-bf1b-ea8b8adb06f5_1920x2880.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!koik!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F09671ebf-1c6c-4eec-bf1b-ea8b8adb06f5_1920x2880.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em><a href="https://www.pexels.com/photo/birthday-cake-1684039/">Photo by Daria Obymaha</a></em></p><div><hr></div><p><em>For more Python resources, you can also visit</em> <em><a href="https://realpython.com?utm_source=the-python-coding-stack">Real Python</a>&#8212;you may even stumble on one of my own articles or courses there!</em></p><p><em>Also, are you interested in technical writing? You&#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at</em> <em><a href="http://stephengruppetta.com/breaking-the-rules">Breaking the Rules</a></em>.</p><p><em>And you can find out more about me at</em> <em><a href="https://stephengruppetta.com?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p>]]></content:encoded></item><item><title><![CDATA[The Weird and Wonderful World of Descriptors in Python • The Price is Right]]></title><description><![CDATA[The Weird and Wonderful World of Descriptors in Python &#8226; Let&#8217;s demystify one of the trickiest topics around]]></description><link>https://www.thepythoncodingstack.com/p/python-descriptors-the-price-is-right</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/python-descriptors-the-price-is-right</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Sun, 15 Mar 2026 21:27:03 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Ohw0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F715dbc5d-d1fa-4ff2-91f0-d41aa7d85dc3_4288x2848.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You have a class called <code>Product</code> that has an attribute called <code>.selling_price</code>. You also have an instance of <code>Product</code> assigned to the variable name <code>coffee</code>.</p><p>What happens when you write <code>coffee.selling_price</code>?</p><p>Sure, I know you know. The code returns the value of the <code>.selling_price</code> attribute. But what <em>really</em> happens behind the scenes?</p><p>Python operations rely on special methods. The dot you use to access attributes is no exception. But strange and weird things can happen when you use the dot.</p><p>Normally, the <code>Product</code> class determines what happens when you write <code>coffee.selling_price</code>. It checks whether the instance has a <code>.selling_price</code> attribute, typically by looking in its <code>.__dict__</code> dictionary of attributes. If the instance doesn&#8217;t have this attribute, Python checks whether it&#8217;s a class attribute.</p><p>But sometimes the class doesn&#8217;t get to decide what happens when you write <code>coffee.selling_price</code>. The object in <code>.selling_price</code> could take control of the attribute lookup.</p><p>Let&#8217;s explore the weird and wonderful world of <strong>descriptors</strong> in Python. For such a long time, I struggled to understand descriptors. But, as with many topics, at some stage, everything clicks. The story makes sense. Hopefully, this article can help you make sense of descriptors.</p><p>But first, we&#8217;ll start with properties.</p><h2><strong>The </strong><code>Product</code><strong> Class</strong></h2><p>Let&#8217;s create a <code>Product</code> class to deal with products for sale in a shop:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BfEe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e1dde7-b944-4d59-a580-18261074e809_1254x378.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BfEe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e1dde7-b944-4d59-a580-18261074e809_1254x378.png 424w, https://substackcdn.com/image/fetch/$s_!BfEe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e1dde7-b944-4d59-a580-18261074e809_1254x378.png 848w, https://substackcdn.com/image/fetch/$s_!BfEe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e1dde7-b944-4d59-a580-18261074e809_1254x378.png 1272w, https://substackcdn.com/image/fetch/$s_!BfEe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e1dde7-b944-4d59-a580-18261074e809_1254x378.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!BfEe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e1dde7-b944-4d59-a580-18261074e809_1254x378.png" width="1254" height="378" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d0e1dde7-b944-4d59-a580-18261074e809_1254x378.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:378,&quot;width&quot;:1254,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:38992,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e1dde7-b944-4d59-a580-18261074e809_1254x378.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!BfEe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e1dde7-b944-4d59-a580-18261074e809_1254x378.png 424w, https://substackcdn.com/image/fetch/$s_!BfEe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e1dde7-b944-4d59-a580-18261074e809_1254x378.png 848w, https://substackcdn.com/image/fetch/$s_!BfEe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e1dde7-b944-4d59-a580-18261074e809_1254x378.png 1272w, https://substackcdn.com/image/fetch/$s_!BfEe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0e1dde7-b944-4d59-a580-18261074e809_1254x378.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">All code blocks are available in text format at the end of this article &#8226; #1</figcaption></figure></div><p>You create a <code>Product</code> instance by providing a name, a cost price, and a selling price. There&#8217;s a data attribute for each of these values. There&#8217;s also a <code>.profit_margin</code> data attribute.</p><p>However, there&#8217;s a serious flaw in this code. Have you spotted it?</p><p>Let&#8217;s try it out:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eLIA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc827430c-0b82-4f79-9be6-4761d4295b18_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!eLIA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc827430c-0b82-4f79-9be6-4761d4295b18_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!eLIA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc827430c-0b82-4f79-9be6-4761d4295b18_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!eLIA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc827430c-0b82-4f79-9be6-4761d4295b18_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!eLIA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc827430c-0b82-4f79-9be6-4761d4295b18_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c827430c-0b82-4f79-9be6-4761d4295b18_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:21022,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc827430c-0b82-4f79-9be6-4761d4295b18_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eLIA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc827430c-0b82-4f79-9be6-4761d4295b18_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!eLIA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc827430c-0b82-4f79-9be6-4761d4295b18_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!eLIA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc827430c-0b82-4f79-9be6-4761d4295b18_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!eLIA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc827430c-0b82-4f79-9be6-4761d4295b18_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#2</figcaption></figure></div><p>This code outputs the following:</p><pre><code>coffee.profit_margin=2.0</code></pre><p>So far, so good. But now try the following update:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!35yN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c46b05-c06b-44f4-bee9-66a43c5caacc_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!35yN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c46b05-c06b-44f4-bee9-66a43c5caacc_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!35yN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c46b05-c06b-44f4-bee9-66a43c5caacc_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!35yN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c46b05-c06b-44f4-bee9-66a43c5caacc_1200x294.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!35yN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c46b05-c06b-44f4-bee9-66a43c5caacc_1200x294.png" width="1200" height="294" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e8c46b05-c06b-44f4-bee9-66a43c5caacc_1200x294.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:294,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26865,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c46b05-c06b-44f4-bee9-66a43c5caacc_1200x294.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!35yN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c46b05-c06b-44f4-bee9-66a43c5caacc_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!35yN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c46b05-c06b-44f4-bee9-66a43c5caacc_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!35yN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c46b05-c06b-44f4-bee9-66a43c5caacc_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!35yN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c46b05-c06b-44f4-bee9-66a43c5caacc_1200x294.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#3</figcaption></figure></div><p>Here&#8217;s the output from this version:</p><pre><code>coffee.profit_margin=2.0</code></pre><p>The profit margin still shows as &#163;2. This is incorrect since you modified the selling price to &#163;5.50 soon after creating the <code>Product</code> instance. The cost price is still &#163;2.50. Therefore, the profit margin should be &#163;3.</p><p>The <code>.profit_margin</code> data attribute is only calculated once in the <code>.__init__()</code> method, which is called when you create and initialise the object. The code doesn&#8217;t update the <code>.profit_margin</code> data attribute when you update the selling price.</p><p>You can choose to use a method instead of a data attribute, say <code>.calculate_profit_margin()</code>. However, you think the class&#8217;s interface would be neater and easier to use if <code>.profit_margin</code> were still a data attribute like <code>.cost_price</code> and <code>.selling_price</code>.</p><p>The solution is to create a <em>property</em>. I promise that this is related to this week&#8217;s topic &#8211; properties are a great stepping stone towards descriptors:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GaoC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37ae6495-b11e-4560-b937-cb01275a7887_1200x672.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GaoC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37ae6495-b11e-4560-b937-cb01275a7887_1200x672.png 424w, https://substackcdn.com/image/fetch/$s_!GaoC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37ae6495-b11e-4560-b937-cb01275a7887_1200x672.png 848w, https://substackcdn.com/image/fetch/$s_!GaoC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37ae6495-b11e-4560-b937-cb01275a7887_1200x672.png 1272w, https://substackcdn.com/image/fetch/$s_!GaoC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37ae6495-b11e-4560-b937-cb01275a7887_1200x672.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!GaoC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37ae6495-b11e-4560-b937-cb01275a7887_1200x672.png" width="1200" height="672" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/37ae6495-b11e-4560-b937-cb01275a7887_1200x672.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:672,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:73998,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37ae6495-b11e-4560-b937-cb01275a7887_1200x672.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GaoC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37ae6495-b11e-4560-b937-cb01275a7887_1200x672.png 424w, https://substackcdn.com/image/fetch/$s_!GaoC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37ae6495-b11e-4560-b937-cb01275a7887_1200x672.png 848w, https://substackcdn.com/image/fetch/$s_!GaoC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37ae6495-b11e-4560-b937-cb01275a7887_1200x672.png 1272w, https://substackcdn.com/image/fetch/$s_!GaoC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37ae6495-b11e-4560-b937-cb01275a7887_1200x672.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#4</figcaption></figure></div><p>You start by creating a method called <code>.profit_margin()</code>. Note how you name this in the style of a data attribute rather than a method &#8211; it describes data rather than an action. However, you use the decorator <code>@property</code> to create a <code>property</code> object.</p><p>Whenever you access the <code>.profit_margin</code> attribute, without the parentheses, Python calls the <code>.profit_margin()</code> method behind the scenes. Therefore, each time you access the attribute <code>.profit_margin</code>, which is now a property, Python calculates and returns the current value:</p><pre><code>coffee.profit_margin=3.0</code></pre><p>The <code>.profit_margin</code> attribute looks and feels like a data attribute but behaves like a method.</p><p>There&#8217;s another benefit in this case. This attribute is read-only:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5ekJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f6e17b-5386-4bec-8d50-f9c8d8eba187_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!5ekJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f6e17b-5386-4bec-8d50-f9c8d8eba187_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!5ekJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f6e17b-5386-4bec-8d50-f9c8d8eba187_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!5ekJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f6e17b-5386-4bec-8d50-f9c8d8eba187_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!5ekJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f6e17b-5386-4bec-8d50-f9c8d8eba187_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d5f6e17b-5386-4bec-8d50-f9c8d8eba187_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8828,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f6e17b-5386-4bec-8d50-f9c8d8eba187_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5ekJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f6e17b-5386-4bec-8d50-f9c8d8eba187_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!5ekJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f6e17b-5386-4bec-8d50-f9c8d8eba187_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!5ekJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f6e17b-5386-4bec-8d50-f9c8d8eba187_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!5ekJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5f6e17b-5386-4bec-8d50-f9c8d8eba187_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#5</figcaption></figure></div><p>You try to assign a new value to the <code>.profit_margin</code> attribute, but Python raises an error:</p><pre><code>Traceback (most recent call last):
  ...
    coffee.profit_margin = 4
    ^^^^^^^^^^^^^^^^^^^^
AttributeError: property &#8216;profit_margin&#8217; of 
    &#8216;Product&#8217; object has no setter</code></pre><p>In this case, this is the desired behaviour since a user shouldn&#8217;t be able to set the profit margin directly. It should be calculated from the cost price and the selling price.</p><h3><strong>Validating Prices</strong></h3><p>But let&#8217;s move to the central theme of this article. You want to add validation to the prices. For example, you want to ensure they can&#8217;t be below zero.</p><p>Let&#8217;s work on the cost price only for now. The following version won&#8217;t work for the same reason you saw earlier:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ctat!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94061c25-f165-432e-a68a-e5a90140e452_1200x672.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ctat!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94061c25-f165-432e-a68a-e5a90140e452_1200x672.png 424w, https://substackcdn.com/image/fetch/$s_!ctat!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94061c25-f165-432e-a68a-e5a90140e452_1200x672.png 848w, https://substackcdn.com/image/fetch/$s_!ctat!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94061c25-f165-432e-a68a-e5a90140e452_1200x672.png 1272w, https://substackcdn.com/image/fetch/$s_!ctat!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94061c25-f165-432e-a68a-e5a90140e452_1200x672.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ctat!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94061c25-f165-432e-a68a-e5a90140e452_1200x672.png" width="1200" height="672" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/94061c25-f165-432e-a68a-e5a90140e452_1200x672.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:672,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:69143,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94061c25-f165-432e-a68a-e5a90140e452_1200x672.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ctat!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94061c25-f165-432e-a68a-e5a90140e452_1200x672.png 424w, https://substackcdn.com/image/fetch/$s_!ctat!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94061c25-f165-432e-a68a-e5a90140e452_1200x672.png 848w, https://substackcdn.com/image/fetch/$s_!ctat!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94061c25-f165-432e-a68a-e5a90140e452_1200x672.png 1272w, https://substackcdn.com/image/fetch/$s_!ctat!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94061c25-f165-432e-a68a-e5a90140e452_1200x672.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#6</figcaption></figure></div><p>You add the validation in the <code>.__init__()</code> method. This will apply only to the initialisation process, not to any later changes to the cost price.</p><p>So, the validation works fine in this case:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IqGl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb40667df-71a7-44e0-a9b2-8a5cb514484f_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!IqGl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb40667df-71a7-44e0-a9b2-8a5cb514484f_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!IqGl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb40667df-71a7-44e0-a9b2-8a5cb514484f_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!IqGl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb40667df-71a7-44e0-a9b2-8a5cb514484f_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!IqGl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb40667df-71a7-44e0-a9b2-8a5cb514484f_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b40667df-71a7-44e0-a9b2-8a5cb514484f_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12126,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb40667df-71a7-44e0-a9b2-8a5cb514484f_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IqGl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb40667df-71a7-44e0-a9b2-8a5cb514484f_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!IqGl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb40667df-71a7-44e0-a9b2-8a5cb514484f_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!IqGl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb40667df-71a7-44e0-a9b2-8a5cb514484f_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!IqGl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb40667df-71a7-44e0-a9b2-8a5cb514484f_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#7</figcaption></figure></div><p>This raises the exception:</p><pre><code>Traceback (most recent call last):
  ...
ValueError: Cost price cannot be negative</code></pre><p>However, the code won&#8217;t stop you from changing the cost price to a negative value later:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CWwp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0a38925-192b-4880-bbcd-acdc578b574e_1308x294.png 424w, https://substackcdn.com/image/fetch/$s_!CWwp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0a38925-192b-4880-bbcd-acdc578b574e_1308x294.png 848w, https://substackcdn.com/image/fetch/$s_!CWwp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0a38925-192b-4880-bbcd-acdc578b574e_1308x294.png 1272w, https://substackcdn.com/image/fetch/$s_!CWwp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0a38925-192b-4880-bbcd-acdc578b574e_1308x294.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!CWwp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0a38925-192b-4880-bbcd-acdc578b574e_1308x294.png" width="1308" height="294" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b0a38925-192b-4880-bbcd-acdc578b574e_1308x294.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:294,&quot;width&quot;:1308,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29677,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0a38925-192b-4880-bbcd-acdc578b574e_1308x294.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CWwp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0a38925-192b-4880-bbcd-acdc578b574e_1308x294.png 424w, https://substackcdn.com/image/fetch/$s_!CWwp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0a38925-192b-4880-bbcd-acdc578b574e_1308x294.png 848w, https://substackcdn.com/image/fetch/$s_!CWwp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0a38925-192b-4880-bbcd-acdc578b574e_1308x294.png 1272w, https://substackcdn.com/image/fetch/$s_!CWwp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0a38925-192b-4880-bbcd-acdc578b574e_1308x294.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#8</figcaption></figure></div><p>Here&#8217;s the output:</p><pre><code>coffee.cost_price=-1</code></pre><p>Solution? Create a property:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8tzj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44ca994d-8612-4f6f-8ce0-261f8b4979f6_1200x756.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8tzj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44ca994d-8612-4f6f-8ce0-261f8b4979f6_1200x756.png 424w, https://substackcdn.com/image/fetch/$s_!8tzj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44ca994d-8612-4f6f-8ce0-261f8b4979f6_1200x756.png 848w, https://substackcdn.com/image/fetch/$s_!8tzj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44ca994d-8612-4f6f-8ce0-261f8b4979f6_1200x756.png 1272w, https://substackcdn.com/image/fetch/$s_!8tzj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44ca994d-8612-4f6f-8ce0-261f8b4979f6_1200x756.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!8tzj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44ca994d-8612-4f6f-8ce0-261f8b4979f6_1200x756.png" width="1200" height="756" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/44ca994d-8612-4f6f-8ce0-261f8b4979f6_1200x756.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:756,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59455,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44ca994d-8612-4f6f-8ce0-261f8b4979f6_1200x756.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8tzj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44ca994d-8612-4f6f-8ce0-261f8b4979f6_1200x756.png 424w, https://substackcdn.com/image/fetch/$s_!8tzj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44ca994d-8612-4f6f-8ce0-261f8b4979f6_1200x756.png 848w, https://substackcdn.com/image/fetch/$s_!8tzj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44ca994d-8612-4f6f-8ce0-261f8b4979f6_1200x756.png 1272w, https://substackcdn.com/image/fetch/$s_!8tzj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44ca994d-8612-4f6f-8ce0-261f8b4979f6_1200x756.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#9</figcaption></figure></div><p>Note that I removed the validation code from the <code>.__init__()</code> method. The definition of the <code>.cost_price</code> data attribute in <code>.__init__()</code> has also gone.</p><p>But what do you include in the <code>cost_price()</code> method? Unlike the <code>.profit_margin</code> property, which can be calculated from existing attributes, you still need somewhere to store the current value for the cost price. You still need a data attribute that you create in the <code>.__init__()</code> method.</p><p>You can create a <em>non-public</em> data attribute:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YjSH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a060f66-3fda-4a4e-bd9c-3d9465d1f03c_1200x756.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YjSH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a060f66-3fda-4a4e-bd9c-3d9465d1f03c_1200x756.png 424w, https://substackcdn.com/image/fetch/$s_!YjSH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a060f66-3fda-4a4e-bd9c-3d9465d1f03c_1200x756.png 848w, https://substackcdn.com/image/fetch/$s_!YjSH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a060f66-3fda-4a4e-bd9c-3d9465d1f03c_1200x756.png 1272w, https://substackcdn.com/image/fetch/$s_!YjSH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a060f66-3fda-4a4e-bd9c-3d9465d1f03c_1200x756.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!YjSH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a060f66-3fda-4a4e-bd9c-3d9465d1f03c_1200x756.png" width="1200" height="756" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8a060f66-3fda-4a4e-bd9c-3d9465d1f03c_1200x756.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:756,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:69170,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a060f66-3fda-4a4e-bd9c-3d9465d1f03c_1200x756.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!YjSH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a060f66-3fda-4a4e-bd9c-3d9465d1f03c_1200x756.png 424w, https://substackcdn.com/image/fetch/$s_!YjSH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a060f66-3fda-4a4e-bd9c-3d9465d1f03c_1200x756.png 848w, https://substackcdn.com/image/fetch/$s_!YjSH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a060f66-3fda-4a4e-bd9c-3d9465d1f03c_1200x756.png 1272w, https://substackcdn.com/image/fetch/$s_!YjSH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a060f66-3fda-4a4e-bd9c-3d9465d1f03c_1200x756.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#10</figcaption></figure></div><p>The data attribute <code>._cost_price</code> has a leading underscore, which is a Python convention indicating that the user shouldn&#8217;t access this attribute directly. Python doesn&#8217;t prevent the user from accessing this non-public attribute, but the leading underscore shows the programmer&#8217;s intent that this attribute should be left alone when using the class.</p><p>Whenever you access the <code>.cost_price</code> property, Python executes the code you define in the method that you decorate with the <code>@property</code> decorator. In this case, Python returns the value of the <code>._cost_price</code> data attribute.</p><p>Therefore, you still have a data attribute &#8211; the one with the leading underscore. However, the user doesn&#8217;t access it directly. Instead, they access it through the property <code>.cost_price</code>:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YkMa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fd11392-2574-4e88-8cec-fbc12fcd2a69_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!YkMa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fd11392-2574-4e88-8cec-fbc12fcd2a69_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!YkMa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fd11392-2574-4e88-8cec-fbc12fcd2a69_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!YkMa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fd11392-2574-4e88-8cec-fbc12fcd2a69_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!YkMa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fd11392-2574-4e88-8cec-fbc12fcd2a69_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1fd11392-2574-4e88-8cec-fbc12fcd2a69_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19691,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fd11392-2574-4e88-8cec-fbc12fcd2a69_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!YkMa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fd11392-2574-4e88-8cec-fbc12fcd2a69_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!YkMa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fd11392-2574-4e88-8cec-fbc12fcd2a69_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!YkMa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fd11392-2574-4e88-8cec-fbc12fcd2a69_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!YkMa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fd11392-2574-4e88-8cec-fbc12fcd2a69_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#11</figcaption></figure></div><p>Here&#8217;s the output from this code:</p><pre><code>coffee.cost_price=2.5</code></pre><p>However, this makes the cost price read-only. That&#8217;s not what you&#8217;re trying to achieve:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!n8r3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d078d4f-b440-43be-8273-263cbf93b0e4_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!n8r3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d078d4f-b440-43be-8273-263cbf93b0e4_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!n8r3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d078d4f-b440-43be-8273-263cbf93b0e4_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!n8r3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d078d4f-b440-43be-8273-263cbf93b0e4_1200x294.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!n8r3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d078d4f-b440-43be-8273-263cbf93b0e4_1200x294.png" width="1200" height="294" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9d078d4f-b440-43be-8273-263cbf93b0e4_1200x294.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:294,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24537,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d078d4f-b440-43be-8273-263cbf93b0e4_1200x294.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!n8r3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d078d4f-b440-43be-8273-263cbf93b0e4_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!n8r3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d078d4f-b440-43be-8273-263cbf93b0e4_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!n8r3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d078d4f-b440-43be-8273-263cbf93b0e4_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!n8r3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d078d4f-b440-43be-8273-263cbf93b0e4_1200x294.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#12</figcaption></figure></div><p>This raises an error since you can&#8217;t assign values to the <code>.cost_price</code> property:</p><pre><code>Traceback (most recent call last):
  ...
    coffee.cost_price = 3
    ^^^^^^^^^^^^^^^^^
AttributeError: property &#8216;cost_price&#8217; of 
    &#8216;Product&#8217; object has no setter</code></pre><p>Note what the error message says: <code>'Product' object has no setter</code>. The setter is the method that allows you to set the value of a property. But you can add this setter to your code:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FsQ2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63aaf448-0f80-4c74-8878-f3886e8cf715_1200x1092.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FsQ2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63aaf448-0f80-4c74-8878-f3886e8cf715_1200x1092.png 424w, https://substackcdn.com/image/fetch/$s_!FsQ2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63aaf448-0f80-4c74-8878-f3886e8cf715_1200x1092.png 848w, https://substackcdn.com/image/fetch/$s_!FsQ2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63aaf448-0f80-4c74-8878-f3886e8cf715_1200x1092.png 1272w, https://substackcdn.com/image/fetch/$s_!FsQ2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63aaf448-0f80-4c74-8878-f3886e8cf715_1200x1092.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!FsQ2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63aaf448-0f80-4c74-8878-f3886e8cf715_1200x1092.png" width="1200" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/63aaf448-0f80-4c74-8878-f3886e8cf715_1200x1092.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:125707,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63aaf448-0f80-4c74-8878-f3886e8cf715_1200x1092.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FsQ2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63aaf448-0f80-4c74-8878-f3886e8cf715_1200x1092.png 424w, https://substackcdn.com/image/fetch/$s_!FsQ2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63aaf448-0f80-4c74-8878-f3886e8cf715_1200x1092.png 848w, https://substackcdn.com/image/fetch/$s_!FsQ2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63aaf448-0f80-4c74-8878-f3886e8cf715_1200x1092.png 1272w, https://substackcdn.com/image/fetch/$s_!FsQ2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63aaf448-0f80-4c74-8878-f3886e8cf715_1200x1092.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#13</figcaption></figure></div><p>There are no issues with setting the value for <code>coffee.cost_price</code> now that you have a setter, too. And the validation works when you try to set the cost price value to a negative value:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!82VK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff394673b-ab91-40e1-958a-00f274c6cc32_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!82VK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff394673b-ab91-40e1-958a-00f274c6cc32_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!82VK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff394673b-ab91-40e1-958a-00f274c6cc32_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!82VK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff394673b-ab91-40e1-958a-00f274c6cc32_1200x294.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!82VK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff394673b-ab91-40e1-958a-00f274c6cc32_1200x294.png" width="1200" height="294" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f394673b-ab91-40e1-958a-00f274c6cc32_1200x294.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:294,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24571,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff394673b-ab91-40e1-958a-00f274c6cc32_1200x294.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!82VK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff394673b-ab91-40e1-958a-00f274c6cc32_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!82VK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff394673b-ab91-40e1-958a-00f274c6cc32_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!82VK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff394673b-ab91-40e1-958a-00f274c6cc32_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!82VK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff394673b-ab91-40e1-958a-00f274c6cc32_1200x294.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#14</figcaption></figure></div><p>This raises an error:</p><pre><code>Traceback (most recent call last):
  ...
    coffee.cost_price = -3
    ^^^^^^^^^^^^^^^^^
ValueError: Cost price cannot be negative</code></pre><p>However, there&#8217;s still a flaw in this code. The negative cost price validation still fails in some circumstances. Can you figure out when it fails?</p><p>Try this code:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hKTL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a9dd867-58b4-4995-a531-85a528c89085_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!hKTL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a9dd867-58b4-4995-a531-85a528c89085_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!hKTL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a9dd867-58b4-4995-a531-85a528c89085_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!hKTL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a9dd867-58b4-4995-a531-85a528c89085_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!hKTL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a9dd867-58b4-4995-a531-85a528c89085_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7a9dd867-58b4-4995-a531-85a528c89085_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19708,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a9dd867-58b4-4995-a531-85a528c89085_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hKTL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a9dd867-58b4-4995-a531-85a528c89085_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!hKTL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a9dd867-58b4-4995-a531-85a528c89085_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!hKTL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a9dd867-58b4-4995-a531-85a528c89085_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!hKTL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a9dd867-58b4-4995-a531-85a528c89085_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#15</figcaption></figure></div><p>Here&#8217;s the output:</p><pre><code>coffee.cost_price=-2.5</code></pre><p>Validation occurs only when you set the <code>cost_price</code> attribute. However, look carefully at the code in your <code>.__init__()</code> method. It still sets the actual data attribute <code>._cost_price</code> directly. This bypasses the verification.</p><p>Luckily, there&#8217;s an easy fix:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gSws!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b3fb4c-e3e2-4614-bde3-92903788a708_1200x1008.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gSws!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b3fb4c-e3e2-4614-bde3-92903788a708_1200x1008.png 424w, https://substackcdn.com/image/fetch/$s_!gSws!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b3fb4c-e3e2-4614-bde3-92903788a708_1200x1008.png 848w, https://substackcdn.com/image/fetch/$s_!gSws!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b3fb4c-e3e2-4614-bde3-92903788a708_1200x1008.png 1272w, https://substackcdn.com/image/fetch/$s_!gSws!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b3fb4c-e3e2-4614-bde3-92903788a708_1200x1008.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!gSws!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b3fb4c-e3e2-4614-bde3-92903788a708_1200x1008.png" width="1200" height="1008" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d6b3fb4c-e3e2-4614-bde3-92903788a708_1200x1008.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1008,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:105722,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b3fb4c-e3e2-4614-bde3-92903788a708_1200x1008.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gSws!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b3fb4c-e3e2-4614-bde3-92903788a708_1200x1008.png 424w, https://substackcdn.com/image/fetch/$s_!gSws!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b3fb4c-e3e2-4614-bde3-92903788a708_1200x1008.png 848w, https://substackcdn.com/image/fetch/$s_!gSws!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b3fb4c-e3e2-4614-bde3-92903788a708_1200x1008.png 1272w, https://substackcdn.com/image/fetch/$s_!gSws!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b3fb4c-e3e2-4614-bde3-92903788a708_1200x1008.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#16</figcaption></figure></div><p>You can use the property in the <code>.__init__()</code> method, too. This triggers the property&#8217;s setter, which creates the <code>._cost_price</code> data attribute and stores the value in it.</p><h3><strong>Avoiding </strong><code>float</code><strong> for prices</strong></h3><p>Let&#8217;s add a bit more to the validation code. It&#8217;s best not to use <code>float</code> when dealing with prices since floats suffer from rounding errors, which may lead to issues when dealing with money (especially if the rounding goes against you!)</p><p>Here&#8217;s the classic example showing the floating-point rounding errors:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OYTG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce41330d-0f74-4906-aab8-379362886a70_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!OYTG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce41330d-0f74-4906-aab8-379362886a70_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!OYTG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce41330d-0f74-4906-aab8-379362886a70_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!OYTG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce41330d-0f74-4906-aab8-379362886a70_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!OYTG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce41330d-0f74-4906-aab8-379362886a70_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ce41330d-0f74-4906-aab8-379362886a70_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6548,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce41330d-0f74-4906-aab8-379362886a70_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!OYTG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce41330d-0f74-4906-aab8-379362886a70_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!OYTG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce41330d-0f74-4906-aab8-379362886a70_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!OYTG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce41330d-0f74-4906-aab8-379362886a70_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!OYTG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce41330d-0f74-4906-aab8-379362886a70_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#17</figcaption></figure></div><p>Some floats cannot be represented precisely in binary, leading to rounding errors.</p><p>One option is to work in cents or pence or whatever the smaller unit of the currency you&#8217;re using. This means you&#8217;re working with integers. However, an alternative is to use <code>decimal.Decimal</code> instead:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lrj0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8838be9d-2dae-498a-a9bd-949e0b60d913_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!lrj0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8838be9d-2dae-498a-a9bd-949e0b60d913_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!lrj0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8838be9d-2dae-498a-a9bd-949e0b60d913_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!lrj0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8838be9d-2dae-498a-a9bd-949e0b60d913_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!lrj0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8838be9d-2dae-498a-a9bd-949e0b60d913_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8838be9d-2dae-498a-a9bd-949e0b60d913_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19745,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8838be9d-2dae-498a-a9bd-949e0b60d913_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lrj0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8838be9d-2dae-498a-a9bd-949e0b60d913_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!lrj0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8838be9d-2dae-498a-a9bd-949e0b60d913_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!lrj0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8838be9d-2dae-498a-a9bd-949e0b60d913_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!lrj0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8838be9d-2dae-498a-a9bd-949e0b60d913_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#18</figcaption></figure></div><p>Note that you should pass the floats as strings to <code>Decimal</code> and let the <code>Decimal</code> class deal with that. If you use <code>float</code> objects, you&#8217;ve already included the rounding errors in your starting value!</p><p>Let&#8217;s make sure the cost price uses <code>Decimal</code> internally without forcing the user to use this data type:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vrqm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85ca0f2-18c1-4576-9c41-b33b72d36b29_1200x1260.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vrqm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85ca0f2-18c1-4576-9c41-b33b72d36b29_1200x1260.png 424w, https://substackcdn.com/image/fetch/$s_!vrqm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85ca0f2-18c1-4576-9c41-b33b72d36b29_1200x1260.png 848w, https://substackcdn.com/image/fetch/$s_!vrqm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85ca0f2-18c1-4576-9c41-b33b72d36b29_1200x1260.png 1272w, https://substackcdn.com/image/fetch/$s_!vrqm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85ca0f2-18c1-4576-9c41-b33b72d36b29_1200x1260.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!vrqm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85ca0f2-18c1-4576-9c41-b33b72d36b29_1200x1260.png" width="1200" height="1260" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f85ca0f2-18c1-4576-9c41-b33b72d36b29_1200x1260.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1260,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:140665,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85ca0f2-18c1-4576-9c41-b33b72d36b29_1200x1260.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vrqm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85ca0f2-18c1-4576-9c41-b33b72d36b29_1200x1260.png 424w, https://substackcdn.com/image/fetch/$s_!vrqm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85ca0f2-18c1-4576-9c41-b33b72d36b29_1200x1260.png 848w, https://substackcdn.com/image/fetch/$s_!vrqm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85ca0f2-18c1-4576-9c41-b33b72d36b29_1200x1260.png 1272w, https://substackcdn.com/image/fetch/$s_!vrqm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85ca0f2-18c1-4576-9c41-b33b72d36b29_1200x1260.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#19</figcaption></figure></div><p>In true duck typing fashion, rather than choosing which data types you&#8217;re willing to accept when setting the cost price, you try to convert the input value to <code>Decimal</code>. Any data type that can be converted to <code>Decimal</code> is acceptable. The <code>Decimal</code> type raises an <code>InvalidOperation</code> exception, defined within <code>decimal</code>, when you pass an unacceptable type. So, you use <code>InvalidOperation</code> in the <code>except</code> block.</p><p>You probably want to perform the same validation for the selling price, too:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NKMn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341594c2-3baa-4e2f-87e4-1b09f9fc4cb8_1236x1764.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NKMn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341594c2-3baa-4e2f-87e4-1b09f9fc4cb8_1236x1764.png 424w, https://substackcdn.com/image/fetch/$s_!NKMn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341594c2-3baa-4e2f-87e4-1b09f9fc4cb8_1236x1764.png 848w, https://substackcdn.com/image/fetch/$s_!NKMn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341594c2-3baa-4e2f-87e4-1b09f9fc4cb8_1236x1764.png 1272w, https://substackcdn.com/image/fetch/$s_!NKMn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341594c2-3baa-4e2f-87e4-1b09f9fc4cb8_1236x1764.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!NKMn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341594c2-3baa-4e2f-87e4-1b09f9fc4cb8_1236x1764.png" width="1236" height="1764" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/341594c2-3baa-4e2f-87e4-1b09f9fc4cb8_1236x1764.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1764,&quot;width&quot;:1236,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:223241,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341594c2-3baa-4e2f-87e4-1b09f9fc4cb8_1236x1764.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NKMn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341594c2-3baa-4e2f-87e4-1b09f9fc4cb8_1236x1764.png 424w, https://substackcdn.com/image/fetch/$s_!NKMn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341594c2-3baa-4e2f-87e4-1b09f9fc4cb8_1236x1764.png 848w, https://substackcdn.com/image/fetch/$s_!NKMn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341594c2-3baa-4e2f-87e4-1b09f9fc4cb8_1236x1764.png 1272w, https://substackcdn.com/image/fetch/$s_!NKMn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341594c2-3baa-4e2f-87e4-1b09f9fc4cb8_1236x1764.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#20</figcaption></figure></div><p>This works. However, there&#8217;s a lot of boilerplate code. And the validation is repeated in two setter methods. Sure, you can create a helper function to avoid the repetition. But what if you had more types of prices to include in your class? Perhaps there&#8217;s a <code>wholesale_price</code> and a <code>discounted_price</code>, say. You&#8217;d have to create properties for each one.</p><p>Before I move on from properties, here&#8217;s a link to another article I wrote about properties: <a href="https://www.thepythoncodingstack.com/p/the-properties-of-python-property">The Properties of Python&#8217;s property </a>.</p><p>Now let&#8217;s explore a different option.</p><h2><strong>Descriptors</strong></h2><p>Spoiler alert: you&#8217;ve already been using descriptors. Python&#8217;s <code>property</code> uses the descriptor protocol behind the scenes. Indeed, you use descriptors each time you access a method, or when you use the <code>@classmethod</code> and <code>@staticmethod</code> decorators.</p><p>But let&#8217;s get back to what happens when you use the dot after an object name, such as <code>coffee.cost_price</code>. The quick answer often states that Python looks at the instance&#8217;s <code>.__dict__</code> attribute, which is a dictionary containing the instance&#8217;s data attributes. If the attribute is not there, Python looks in the class&#8217;s <code>.__dict__</code> to check whether the attribute is a class attribute.</p><p>However, there&#8217;s a bit more happening.</p><p>The <code>.</code> notation calls the <code>.__getattribute__()</code> special method, which looks for the attribute using a broader set of rules and priorities.</p><p>The first thing it looks for is whether the attribute is a descriptor &#8211; technically, a data descriptor.</p><p>But what&#8217;s a descriptor?</p><h3><strong>Creating a descriptor class</strong></h3><p>Let&#8217;s create one. Create a <code>Price</code> class:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VH7I!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46965f12-ab9f-4377-99bb-5506288cd524_1362x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VH7I!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46965f12-ab9f-4377-99bb-5506288cd524_1362x630.png 424w, https://substackcdn.com/image/fetch/$s_!VH7I!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46965f12-ab9f-4377-99bb-5506288cd524_1362x630.png 848w, https://substackcdn.com/image/fetch/$s_!VH7I!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46965f12-ab9f-4377-99bb-5506288cd524_1362x630.png 1272w, https://substackcdn.com/image/fetch/$s_!VH7I!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46965f12-ab9f-4377-99bb-5506288cd524_1362x630.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!VH7I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46965f12-ab9f-4377-99bb-5506288cd524_1362x630.png" width="1362" height="630" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/46965f12-ab9f-4377-99bb-5506288cd524_1362x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:630,&quot;width&quot;:1362,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:35129,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46965f12-ab9f-4377-99bb-5506288cd524_1362x630.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VH7I!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46965f12-ab9f-4377-99bb-5506288cd524_1362x630.png 424w, https://substackcdn.com/image/fetch/$s_!VH7I!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46965f12-ab9f-4377-99bb-5506288cd524_1362x630.png 848w, https://substackcdn.com/image/fetch/$s_!VH7I!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46965f12-ab9f-4377-99bb-5506288cd524_1362x630.png 1272w, https://substackcdn.com/image/fetch/$s_!VH7I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46965f12-ab9f-4377-99bb-5506288cd524_1362x630.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#21</figcaption></figure></div><p>An object that has either of these special methods is a <em>descriptor</em>. (Note that there&#8217;s another special method that&#8217;s part of the descriptor protocol, <code>.__delete__()</code>, which is also sufficient to create a descriptor. I won&#8217;t be discussing <code>.__delete__()</code> in this article.)</p><p>So what? This is where descriptors start getting weird &#8211; yes, right at the start! Let&#8217;s pause with this class and get back to the <code>Product</code> class. Define <code>.cost_price</code> and  <code>.selling_price</code> as <em>class attributes</em> and assign instances of the new <code>Price</code> class to them:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NMUv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff448a06b-1552-4082-92f0-e7aefd86dc15_1200x798.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NMUv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff448a06b-1552-4082-92f0-e7aefd86dc15_1200x798.png 424w, https://substackcdn.com/image/fetch/$s_!NMUv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff448a06b-1552-4082-92f0-e7aefd86dc15_1200x798.png 848w, https://substackcdn.com/image/fetch/$s_!NMUv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff448a06b-1552-4082-92f0-e7aefd86dc15_1200x798.png 1272w, https://substackcdn.com/image/fetch/$s_!NMUv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff448a06b-1552-4082-92f0-e7aefd86dc15_1200x798.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!NMUv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff448a06b-1552-4082-92f0-e7aefd86dc15_1200x798.png" width="1200" height="798" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f448a06b-1552-4082-92f0-e7aefd86dc15_1200x798.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:798,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:64256,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff448a06b-1552-4082-92f0-e7aefd86dc15_1200x798.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NMUv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff448a06b-1552-4082-92f0-e7aefd86dc15_1200x798.png 424w, https://substackcdn.com/image/fetch/$s_!NMUv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff448a06b-1552-4082-92f0-e7aefd86dc15_1200x798.png 848w, https://substackcdn.com/image/fetch/$s_!NMUv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff448a06b-1552-4082-92f0-e7aefd86dc15_1200x798.png 1272w, https://substackcdn.com/image/fetch/$s_!NMUv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff448a06b-1552-4082-92f0-e7aefd86dc15_1200x798.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#22</figcaption></figure></div><p>The rest of the code remains the same except that four methods are gone. These are the getter and setter methods you had defined previously to ensure that <code>.cost_price</code> and <code>.selling_price</code> are properties. You no longer need them. You&#8217;ll see why soon.</p><p>Why define class attributes? Class attributes are created when you define the class, unlike instance attributes, which are created when you create instances. Therefore, the attributes <code>.cost_price</code> and <code>.selling_price</code> exist and are equal to instances of a descriptor class at the time of class definition. This matters, as you&#8217;ll also see shortly.</p><p>So, what&#8217;s special about descriptors? Remember when we discussed how the <code>.</code> triggers the <code>.__getattribute__()</code> special method, which is responsible for looking for the attribute and returning its value? Well, as it happens, Python doesn&#8217;t look in the instance&#8217;s <code>.__dict__</code> attribute first, after all.</p><p>The first thing it checks is whether the attribute is a descriptor. More specifically, it checks whether the attribute is a data descriptor, which is a descriptor that has <code>.__set__()</code> or <code>.__delete__()</code>. But our descriptor class, <code>Price</code>, has a <code>.__set__()</code>, so this priority applies to a <code>Price</code> object.</p><p>Here&#8217;s the key to understanding descriptors:</p><blockquote><p>Since <code>.cost_price</code> and <code>.selling_price</code> are descriptors, the <code>Product</code> class defers to the <code>Price</code> class to decide how to get or set a value for this attribute.</p></blockquote><p>Let me repeat that.</p><p>Normally, the class that defines an instance determines its behaviour. The special methods in <code>Product</code> determine how a <code>Product</code> instance behaves, such as what its string representations are, whether it&#8217;s iterable, whether it has a length, and so on. We haven&#8217;t defined any of these special methods in <code>Product</code>, but if we did, they would determine the object&#8217;s behaviour in those contexts.</p><p>However, when you use the dot on an instance of a <code>Product</code> class, if the attribute you&#8217;re accessing is a descriptor, such as <code>.cost_price</code> and <code>.selling_price</code>, it&#8217;s <code>Price.__get__()</code> and <code>Price.__set__()</code> that determine what to do when you get or set the values of <code>.cost_price</code> and <code>.selling_price</code>.</p><p>Code in the <code>Price</code> class determines what happens when you access an attribute in a <code>Product</code> instance.</p><p>And since you define <code>.cost_price</code> and <code>.selling_price</code> as class attributes that contain descriptors &#8211; instances of <code>Price</code> &#8211; then the descriptor special methods kick in even in <code>Product.__init__()</code> when you write <code>self.cost_price = cost_price</code> and <code>self.selling_price = selling_price</code>.</p><div class="pullquote"><p><em>Do you want to master Python one article at a time? Then don&#8217;t miss out on the articles in The Club, which are exclusive to premium subscribers here on The Python Coding Stack</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p></div><h3><strong>Understanding how descriptors work</strong></h3><p>Let&#8217;s prove this by adding some code to the <code>Price</code> class. This code is here to let us track what&#8217;s happening. You&#8217;ll write &#8220;proper&#8221; code later:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zP4l!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04cd6565-b6a1-4f99-9037-ab864104f0d7_1200x588.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zP4l!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04cd6565-b6a1-4f99-9037-ab864104f0d7_1200x588.png 424w, https://substackcdn.com/image/fetch/$s_!zP4l!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04cd6565-b6a1-4f99-9037-ab864104f0d7_1200x588.png 848w, https://substackcdn.com/image/fetch/$s_!zP4l!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04cd6565-b6a1-4f99-9037-ab864104f0d7_1200x588.png 1272w, https://substackcdn.com/image/fetch/$s_!zP4l!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04cd6565-b6a1-4f99-9037-ab864104f0d7_1200x588.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!zP4l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04cd6565-b6a1-4f99-9037-ab864104f0d7_1200x588.png" width="1200" height="588" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/04cd6565-b6a1-4f99-9037-ab864104f0d7_1200x588.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:588,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:48714,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04cd6565-b6a1-4f99-9037-ab864104f0d7_1200x588.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zP4l!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04cd6565-b6a1-4f99-9037-ab864104f0d7_1200x588.png 424w, https://substackcdn.com/image/fetch/$s_!zP4l!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04cd6565-b6a1-4f99-9037-ab864104f0d7_1200x588.png 848w, https://substackcdn.com/image/fetch/$s_!zP4l!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04cd6565-b6a1-4f99-9037-ab864104f0d7_1200x588.png 1272w, https://substackcdn.com/image/fetch/$s_!zP4l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04cd6565-b6a1-4f99-9037-ab864104f0d7_1200x588.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#23</figcaption></figure></div><p>The <code>.__get__()</code> and <code>.__set__()</code> methods print out phrases to allow you to see when these methods are called. The <code>.__get__()</code> special method also returns a string. A reminder that these special methods are defined in the <code>Price</code> class.</p><p>Here&#8217;s the code so far, which includes a statement in the main scope that creates an instance of the <code>Product</code> class:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8ksU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3b33929-c533-47c1-a7f8-c57f626a01f6_1200x1092.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8ksU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3b33929-c533-47c1-a7f8-c57f626a01f6_1200x1092.png 424w, https://substackcdn.com/image/fetch/$s_!8ksU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3b33929-c533-47c1-a7f8-c57f626a01f6_1200x1092.png 848w, https://substackcdn.com/image/fetch/$s_!8ksU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3b33929-c533-47c1-a7f8-c57f626a01f6_1200x1092.png 1272w, https://substackcdn.com/image/fetch/$s_!8ksU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3b33929-c533-47c1-a7f8-c57f626a01f6_1200x1092.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!8ksU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3b33929-c533-47c1-a7f8-c57f626a01f6_1200x1092.png" width="1200" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d3b33929-c533-47c1-a7f8-c57f626a01f6_1200x1092.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:115622,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3b33929-c533-47c1-a7f8-c57f626a01f6_1200x1092.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8ksU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3b33929-c533-47c1-a7f8-c57f626a01f6_1200x1092.png 424w, https://substackcdn.com/image/fetch/$s_!8ksU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3b33929-c533-47c1-a7f8-c57f626a01f6_1200x1092.png 848w, https://substackcdn.com/image/fetch/$s_!8ksU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3b33929-c533-47c1-a7f8-c57f626a01f6_1200x1092.png 1272w, https://substackcdn.com/image/fetch/$s_!8ksU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3b33929-c533-47c1-a7f8-c57f626a01f6_1200x1092.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#24</figcaption></figure></div><p>Here&#8217;s what you see when you run this code:</p><pre><code>Calling Price.__set__()
Calling Price.__set__()</code></pre><p>The code calls the <code>Price.__set__()</code> method twice: once when creating <code>self.cost_price</code> and another time when creating <code>self.selling_price</code> in <code>Product.__init__()</code>.</p><p>Let&#8217;s also fetch one of the attributes:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dwkx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2bc936e-6411-42ee-a06c-9553d10272c8_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!dwkx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2bc936e-6411-42ee-a06c-9553d10272c8_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!dwkx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2bc936e-6411-42ee-a06c-9553d10272c8_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!dwkx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2bc936e-6411-42ee-a06c-9553d10272c8_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!dwkx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2bc936e-6411-42ee-a06c-9553d10272c8_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a2bc936e-6411-42ee-a06c-9553d10272c8_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19150,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2bc936e-6411-42ee-a06c-9553d10272c8_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dwkx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2bc936e-6411-42ee-a06c-9553d10272c8_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!dwkx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2bc936e-6411-42ee-a06c-9553d10272c8_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!dwkx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2bc936e-6411-42ee-a06c-9553d10272c8_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!dwkx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2bc936e-6411-42ee-a06c-9553d10272c8_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#25</figcaption></figure></div><p>Here&#8217;s the output from this code:</p><pre><code>Calling Price.__set__()
Calling Price.__set__()
Calling Price.__get__()
Price coming soon...</code></pre><p>You still get the two &#8220;breadcrumbs&#8221; that show that the code called <code>Price.__set__()</code> twice. But now you can also see that the code calls <code>Price.__get__()</code> since you wrote <code>print(coffee.selling_price)</code>. And when you access the <code>.selling_price</code> attribute, Python returns the value returned by <code>Price.__get__()</code>, which is the string saying <code>"Price coming soon..."</code>.</p><p>Therefore, attribute access for <code>.selling_price</code> and <code>.cost_price</code>, which are attributes in <code>Product</code>, is now controlled by the <code>Price</code> class, which is the descriptor class.</p><p>Yes, the <code>Price</code> class has taken over control of attribute access in the <code>Product</code> class (but only for the attributes that are descriptors of the <code>Price</code> class, <code>.cost_price</code> and <code>.selling_price</code>.)</p><div><hr></div><p>Let&#8217;s look at the signatures of the <code>.__get__()</code> and <code>.__set__()</code> special methods to figure out how they work. Let&#8217;s focus on <code>.__get__()</code> first. Here&#8217;s the method&#8217;s signature and a few more printouts to help us figure out what&#8217;s what:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oMth!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cca53ba-8111-4d87-b915-9128dcaa068b_1200x504.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oMth!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cca53ba-8111-4d87-b915-9128dcaa068b_1200x504.png 424w, https://substackcdn.com/image/fetch/$s_!oMth!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cca53ba-8111-4d87-b915-9128dcaa068b_1200x504.png 848w, https://substackcdn.com/image/fetch/$s_!oMth!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cca53ba-8111-4d87-b915-9128dcaa068b_1200x504.png 1272w, https://substackcdn.com/image/fetch/$s_!oMth!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cca53ba-8111-4d87-b915-9128dcaa068b_1200x504.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!oMth!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cca53ba-8111-4d87-b915-9128dcaa068b_1200x504.png" width="1200" height="504" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3cca53ba-8111-4d87-b915-9128dcaa068b_1200x504.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:504,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:48439,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cca53ba-8111-4d87-b915-9128dcaa068b_1200x504.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oMth!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cca53ba-8111-4d87-b915-9128dcaa068b_1200x504.png 424w, https://substackcdn.com/image/fetch/$s_!oMth!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cca53ba-8111-4d87-b915-9128dcaa068b_1200x504.png 848w, https://substackcdn.com/image/fetch/$s_!oMth!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cca53ba-8111-4d87-b915-9128dcaa068b_1200x504.png 1272w, https://substackcdn.com/image/fetch/$s_!oMth!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cca53ba-8111-4d87-b915-9128dcaa068b_1200x504.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#26</figcaption></figure></div><p>There are three parameters defined in the signature for <code>.__get__()</code>. You won&#8217;t be surprised to see <code>self</code> as the first parameter, which is the case for all instance methods in classes. There are also two other parameters named <code>instance</code> and <code>owner</code>. Let&#8217;s figure out what they are.</p><p>You&#8217;ve seen earlier that the current code calls <code>.__get__()</code> once when the code evaluates <code>coffee.selling_price</code>, which you use as an argument for <code>print()</code>. Here&#8217;s the output of the code now:</p><pre><code>Calling Price.__set__()
Calling Price.__set__()
Calling Price.__get__()
self=&lt;__main__.Price object at 0x100bc8f50&gt;
instance=&lt;__main__.Product object at 0x100bd9160&gt;
owner=&lt;class &#8216;__main__.Product&#8217;&gt;
Price coming soon...</code></pre><p>The new additions are the printouts on the fourth, fifth, and sixth lines showing the values assigned to the parameters <code>self</code>, <code>instance</code>, and <code>owner</code> in <code>Price.__get__()</code>. First, a reminder that this method is triggered when you run the following expression:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!K0Ia!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f58be39-97c5-4384-8a64-e7ff56d089a7_1200x168.png 424w, https://substackcdn.com/image/fetch/$s_!K0Ia!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f58be39-97c5-4384-8a64-e7ff56d089a7_1200x168.png 848w, https://substackcdn.com/image/fetch/$s_!K0Ia!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f58be39-97c5-4384-8a64-e7ff56d089a7_1200x168.png 1272w, https://substackcdn.com/image/fetch/$s_!K0Ia!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f58be39-97c5-4384-8a64-e7ff56d089a7_1200x168.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!K0Ia!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f58be39-97c5-4384-8a64-e7ff56d089a7_1200x168.png" width="1200" height="168" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f58be39-97c5-4384-8a64-e7ff56d089a7_1200x168.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:168,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7086,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f58be39-97c5-4384-8a64-e7ff56d089a7_1200x168.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!K0Ia!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f58be39-97c5-4384-8a64-e7ff56d089a7_1200x168.png 424w, https://substackcdn.com/image/fetch/$s_!K0Ia!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f58be39-97c5-4384-8a64-e7ff56d089a7_1200x168.png 848w, https://substackcdn.com/image/fetch/$s_!K0Ia!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f58be39-97c5-4384-8a64-e7ff56d089a7_1200x168.png 1272w, https://substackcdn.com/image/fetch/$s_!K0Ia!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f58be39-97c5-4384-8a64-e7ff56d089a7_1200x168.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#27</figcaption></figure></div><p>Here&#8217;s what the outputs for the three parameters show:</p><ul><li><p><code>self=&lt;__main__.Price object ...&gt;</code>:  Therefore, <code>self</code> refers to the instance of the <code>Price</code> class itself, as <code>self</code> always does in classes. This is the descriptor instance. It&#8217;s the same descriptor object the attribute <code>coffee.selling_price</code> invokes (but not the object that <code>coffee.selling_price</code> returns, as you&#8217;ll see soon.)</p></li><li><p><code>instance=&lt;__main__.Product object ...&gt;</code>: This output shows that <code>instance</code> is an object of type <code>Product</code>. It&#8217;s the object that has the descriptor as one of its attributes. In this example, this is the <code>coffee</code> object.</p></li><li><p><code>owner=&lt;__main__.Product&gt;</code>: This is the <code>Product</code> class itself (not an instance of the class).</p></li></ul><p>Therefore, in the expression <code>coffee.selling_price</code>, <code>coffee</code> is the <code>Product</code> instance and <code>.selling_price</code> is the <code>Price</code> instance, which is the descriptor.</p><p>And when Python evaluates the expression <code>coffee.selling_price</code>, it defers the decision of which value to display to the <code>Price</code> class. Python calls <code>Price.__get__()</code> with the following arguments:</p><ul><li><p>The <code>selling_price</code> descriptor is passed to <code>self</code></p></li><li><p>The <code>coffee</code> object, an instance of <code>Product</code>, is passed to <code>instance</code></p></li><li><p>The <code>Product</code> class is passed to <code>owner</code></p></li></ul><p>I&#8217;ve repeated myself in the previous few paragraphs, but that&#8217;s deliberate. I get confused following the flow from the <code>Product</code> class to calling a method in the descriptor class, <code>Price</code>.  I need this repetition. Maybe it helps you, too.</p><p>As a side note, which I&#8217;ll mostly ignore in this article: It&#8217;s possible to call the descriptor using the class rather than an instance, such as the following:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ohYG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b108f9-5e52-44dc-979c-cedd6774f0bf_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!ohYG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b108f9-5e52-44dc-979c-cedd6774f0bf_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!ohYG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b108f9-5e52-44dc-979c-cedd6774f0bf_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!ohYG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b108f9-5e52-44dc-979c-cedd6774f0bf_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ohYG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b108f9-5e52-44dc-979c-cedd6774f0bf_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a7b108f9-5e52-44dc-979c-cedd6774f0bf_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:11181,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b108f9-5e52-44dc-979c-cedd6774f0bf_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ohYG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b108f9-5e52-44dc-979c-cedd6774f0bf_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!ohYG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b108f9-5e52-44dc-979c-cedd6774f0bf_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!ohYG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b108f9-5e52-44dc-979c-cedd6774f0bf_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!ohYG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b108f9-5e52-44dc-979c-cedd6774f0bf_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#28</figcaption></figure></div><p>Here&#8217;s the output from this code:</p><pre><code>Calling Price.__get__()
self=&lt;__main__.Price object at 0x102a79090&gt;
instance=None
owner=&lt;class &#8216;__main__.Product&#8217;&gt;
Price coming soon...</code></pre><p>In this case, <code>None</code> is assigned to <code>instance</code> since there&#8217;s no <code>Product</code> instance involved.</p><div><hr></div><p>Let&#8217;s look at the <code>.__set__()</code> special method now:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_rcE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1152dbac-f8c2-4013-8e68-dd56883a7c73_1200x504.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_rcE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1152dbac-f8c2-4013-8e68-dd56883a7c73_1200x504.png 424w, https://substackcdn.com/image/fetch/$s_!_rcE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1152dbac-f8c2-4013-8e68-dd56883a7c73_1200x504.png 848w, https://substackcdn.com/image/fetch/$s_!_rcE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1152dbac-f8c2-4013-8e68-dd56883a7c73_1200x504.png 1272w, https://substackcdn.com/image/fetch/$s_!_rcE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1152dbac-f8c2-4013-8e68-dd56883a7c73_1200x504.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!_rcE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1152dbac-f8c2-4013-8e68-dd56883a7c73_1200x504.png" width="1200" height="504" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1152dbac-f8c2-4013-8e68-dd56883a7c73_1200x504.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:504,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:42879,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1152dbac-f8c2-4013-8e68-dd56883a7c73_1200x504.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_rcE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1152dbac-f8c2-4013-8e68-dd56883a7c73_1200x504.png 424w, https://substackcdn.com/image/fetch/$s_!_rcE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1152dbac-f8c2-4013-8e68-dd56883a7c73_1200x504.png 848w, https://substackcdn.com/image/fetch/$s_!_rcE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1152dbac-f8c2-4013-8e68-dd56883a7c73_1200x504.png 1272w, https://substackcdn.com/image/fetch/$s_!_rcE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1152dbac-f8c2-4013-8e68-dd56883a7c73_1200x504.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#29</figcaption></figure></div><p>The signature still includes <code>self</code> and <code>instance</code>, and these refer to the same objects as they did in <code>.__get__()</code>. The third parameter is <code>value</code>, which stores the object you want to use when setting the attribute&#8217;s value. Let&#8217;s focus on creating an instance of <code>Product</code> as you did earlier:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Nyih!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d22059-8484-4c5b-8a83-310e7d264c56_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!Nyih!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d22059-8484-4c5b-8a83-310e7d264c56_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!Nyih!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d22059-8484-4c5b-8a83-310e7d264c56_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!Nyih!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d22059-8484-4c5b-8a83-310e7d264c56_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Nyih!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d22059-8484-4c5b-8a83-310e7d264c56_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/06d22059-8484-4c5b-8a83-310e7d264c56_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12153,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d22059-8484-4c5b-8a83-310e7d264c56_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Nyih!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d22059-8484-4c5b-8a83-310e7d264c56_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!Nyih!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d22059-8484-4c5b-8a83-310e7d264c56_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!Nyih!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d22059-8484-4c5b-8a83-310e7d264c56_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!Nyih!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d22059-8484-4c5b-8a83-310e7d264c56_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#30</figcaption></figure></div><p>This code accesses descriptors using the dot notation twice within <code>Product.__init__()</code> since it sets the values of <code>.cost_price</code> and <code>.selling_price</code>, which contain <code>Price</code> objects. Here&#8217;s the output from this code:</p><pre><code>Calling Price.__set__()
self=&lt;__main__.Price object at 0x104cf5010&gt;
instance=&lt;__main__.Product object at 0x104cf5160&gt;
value=3.2

Calling Price.__set__()
self=&lt;__main__.Price object at 0x104ce4f50&gt;
instance=&lt;__main__.Product object at 0x104cf5160&gt;
value=5</code></pre><p>The two calls are triggered by two different descriptors, <code>.cost_price</code> and <code>.selling_price</code>. Therefore, <code>self</code> refers to different objects in these calls, as you can see from the different identity values displayed.</p><p>However, the descriptors belong to the same instance, the <code>Product</code> object you named <code>coffee</code>. You can see that the printout references the same <code>Product</code> object on both occasions.</p><p>The value is the number you&#8217;re trying to assign to the descriptors.</p><h3><strong>Writing the </strong><code>.__get__()</code><strong> and </strong><code>.__set__()</code><strong> methods</strong></h3><p>Now you know how and when Python calls the <code>.__get__()</code> and <code>.__set__()</code> methods, you can add code within them. You can use the same reasoning you used when creating properties earlier.</p><p>To keep track of what&#8217;s happening, let&#8217;s build the code you need in stages.</p><p>First, let&#8217;s focus exclusively on the <code>.cost_price</code> attribute. You&#8217;ll write the validation as if it&#8217;s just for cost price &#8212; and you&#8217;ll soon see why that breaks once selling price joins the party. You&#8217;ll deal with <code>.selling_price</code> later.</p><p>You can use the same validation code you used earlier when working with properties. This code should now go in the <code>.__set__()</code> method within the <code>Price</code> class:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UkS-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc15af7a0-2204-4585-8434-c7d1417945da_1380x966.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UkS-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc15af7a0-2204-4585-8434-c7d1417945da_1380x966.png 424w, https://substackcdn.com/image/fetch/$s_!UkS-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc15af7a0-2204-4585-8434-c7d1417945da_1380x966.png 848w, https://substackcdn.com/image/fetch/$s_!UkS-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc15af7a0-2204-4585-8434-c7d1417945da_1380x966.png 1272w, https://substackcdn.com/image/fetch/$s_!UkS-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc15af7a0-2204-4585-8434-c7d1417945da_1380x966.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!UkS-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc15af7a0-2204-4585-8434-c7d1417945da_1380x966.png" width="1380" height="966" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c15af7a0-2204-4585-8434-c7d1417945da_1380x966.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:966,&quot;width&quot;:1380,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:113195,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc15af7a0-2204-4585-8434-c7d1417945da_1380x966.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UkS-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc15af7a0-2204-4585-8434-c7d1417945da_1380x966.png 424w, https://substackcdn.com/image/fetch/$s_!UkS-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc15af7a0-2204-4585-8434-c7d1417945da_1380x966.png 848w, https://substackcdn.com/image/fetch/$s_!UkS-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc15af7a0-2204-4585-8434-c7d1417945da_1380x966.png 1272w, https://substackcdn.com/image/fetch/$s_!UkS-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc15af7a0-2204-4585-8434-c7d1417945da_1380x966.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#31</figcaption></figure></div><p>Let&#8217;s see what&#8217;s new in this version:</p><ol><li><p>I removed most of the <code>print()</code> calls, which were useful merely to explore what objects are passed to these special methods. I left the ones showing when the code calls these methods for now.</p></li><li><p>The <code>.__set__()</code> method includes the <code>try..except</code> block, which ensures the value is a <code>Decimal</code>, and the <code>if</code> statement, which checks that the value is not negative.</p></li><li><p>The final line in <code>.__set__()</code> sets the <code>._cost_price</code> attribute in the <code>instance</code> object, using the value in <code>value</code>. The function call <code>setattr(instance, "_cost_price", value)</code> is equivalent to <code>instance._cost_price = value</code>. In this case, <code>instance</code> is the <code>Product</code> object, such as <code>coffee</code>. There&#8217;s a good reason to use the <code>setattr()</code> built-in function rather than assigning the value directly using the dot notation. You&#8217;ll find out later when you also deal with the selling price.<br>Note that you use the leading underscore in <code>"_cost_price"</code> since you wish to create the non-public attribute <code>._cost_price</code> rather than assign the value to the <code>.cost_price</code> attribute (the one without the leading underscore).</p></li></ol><p>Let&#8217;s try this out. Note that this code is hard-coded to work only for the <code>.cost_price</code> attribute for now:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eoSj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbb135a5-24f8-4788-8a84-3d0183befd93_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!eoSj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbb135a5-24f8-4788-8a84-3d0183befd93_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!eoSj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbb135a5-24f8-4788-8a84-3d0183befd93_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!eoSj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbb135a5-24f8-4788-8a84-3d0183befd93_1200x294.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!eoSj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbb135a5-24f8-4788-8a84-3d0183befd93_1200x294.png" width="1200" height="294" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cbb135a5-24f8-4788-8a84-3d0183befd93_1200x294.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:294,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:18666,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbb135a5-24f8-4788-8a84-3d0183befd93_1200x294.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eoSj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbb135a5-24f8-4788-8a84-3d0183befd93_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!eoSj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbb135a5-24f8-4788-8a84-3d0183befd93_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!eoSj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbb135a5-24f8-4788-8a84-3d0183befd93_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!eoSj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbb135a5-24f8-4788-8a84-3d0183befd93_1200x294.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#32</figcaption></figure></div><p>Oops! This doesn&#8217;t work:</p><pre><code>Calling Price.__set__()
Calling Price.__set__()
Calling Price.__get__()
None</code></pre><p>The final line shows that the code outputs <code>None</code>. That&#8217;s because <code>coffee.cost_price</code> triggers the <code>Price.__get__()</code> method, which you haven&#8217;t completed yet! You can still check whether we&#8217;re on track by printing the actual data attribute <code>._cost_price</code>:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9b_I!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff38e50ef-72e5-4274-8be7-e79b396653e5_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!9b_I!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff38e50ef-72e5-4274-8be7-e79b396653e5_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!9b_I!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff38e50ef-72e5-4274-8be7-e79b396653e5_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!9b_I!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff38e50ef-72e5-4274-8be7-e79b396653e5_1200x294.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!9b_I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff38e50ef-72e5-4274-8be7-e79b396653e5_1200x294.png" width="1200" height="294" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f38e50ef-72e5-4274-8be7-e79b396653e5_1200x294.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:294,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:18671,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff38e50ef-72e5-4274-8be7-e79b396653e5_1200x294.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9b_I!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff38e50ef-72e5-4274-8be7-e79b396653e5_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!9b_I!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff38e50ef-72e5-4274-8be7-e79b396653e5_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!9b_I!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff38e50ef-72e5-4274-8be7-e79b396653e5_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!9b_I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff38e50ef-72e5-4274-8be7-e79b396653e5_1200x294.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#33</figcaption></figure></div><p>You should avoid accessing non-public attributes outside of the class definition in general. But I give you permission to break the rule here since we&#8217;re merely exploring what&#8217;s happening behind the scenes!</p><pre><code>Calling Price.__set__()
Calling Price.__set__()
5</code></pre><p>Note how there&#8217;s no printout saying <code>"Calling Price.__get__()"</code> now since you&#8217;re accessing the data attribute <code>._cost_price</code>, not the descriptor <code>.cost_price</code>.</p><p>But did you notice that this still shows the wrong value? The cost price should be &#163;3.20. It&#8217;s the selling price that&#8217;s &#163;5.00. Remember how you&#8217;re hard-coding the string <code>"_cost_price"</code> in the call to <code>setattr()</code>? This means that when you try to set the selling price, the code currently overwrites the cost price. But don&#8217;t worry, we&#8217;ll fix this soon.</p><p>Time to focus on <code>.__get__()</code>. Since you created the non-public data attribute <code>._cost_price</code> when setting the value using <code>.__set__()</code>, you can now get the value of that attribute using <code>getattr()</code>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fnVT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0653f8-e2ed-4629-886d-4c60934fe3f2_1380x882.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fnVT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0653f8-e2ed-4629-886d-4c60934fe3f2_1380x882.png 424w, https://substackcdn.com/image/fetch/$s_!fnVT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0653f8-e2ed-4629-886d-4c60934fe3f2_1380x882.png 848w, https://substackcdn.com/image/fetch/$s_!fnVT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0653f8-e2ed-4629-886d-4c60934fe3f2_1380x882.png 1272w, https://substackcdn.com/image/fetch/$s_!fnVT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0653f8-e2ed-4629-886d-4c60934fe3f2_1380x882.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!fnVT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0653f8-e2ed-4629-886d-4c60934fe3f2_1380x882.png" width="1380" height="882" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5a0653f8-e2ed-4629-886d-4c60934fe3f2_1380x882.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:882,&quot;width&quot;:1380,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:118708,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0653f8-e2ed-4629-886d-4c60934fe3f2_1380x882.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fnVT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0653f8-e2ed-4629-886d-4c60934fe3f2_1380x882.png 424w, https://substackcdn.com/image/fetch/$s_!fnVT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0653f8-e2ed-4629-886d-4c60934fe3f2_1380x882.png 848w, https://substackcdn.com/image/fetch/$s_!fnVT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0653f8-e2ed-4629-886d-4c60934fe3f2_1380x882.png 1272w, https://substackcdn.com/image/fetch/$s_!fnVT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a0653f8-e2ed-4629-886d-4c60934fe3f2_1380x882.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#34</figcaption></figure></div><p>Recall that in <code>.__get__()</code>, the parameter <code>instance</code> refers to the <code>Product</code> instance, such as <code>coffee</code>. Therefore, <code>getattr(instance, "_cost_price")</code> is equivalent to <code>coffee._cost_price</code> in this example.</p><p>Let&#8217;s try this out, this time using the <code>.cost_price</code> descriptor directly when accessing the value of the cost price:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VeML!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff30a435a-2254-4920-a03a-16564f619858_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!VeML!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff30a435a-2254-4920-a03a-16564f619858_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!VeML!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff30a435a-2254-4920-a03a-16564f619858_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!VeML!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff30a435a-2254-4920-a03a-16564f619858_1200x294.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!VeML!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff30a435a-2254-4920-a03a-16564f619858_1200x294.png" width="1200" height="294" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f30a435a-2254-4920-a03a-16564f619858_1200x294.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:294,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:18661,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff30a435a-2254-4920-a03a-16564f619858_1200x294.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VeML!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff30a435a-2254-4920-a03a-16564f619858_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!VeML!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff30a435a-2254-4920-a03a-16564f619858_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!VeML!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff30a435a-2254-4920-a03a-16564f619858_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!VeML!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff30a435a-2254-4920-a03a-16564f619858_1200x294.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#35</figcaption></figure></div><p>Here&#8217;s the output showing all the breadcrumbs confirming that two attributes are set and that you fetch the value of one of them once. The output also shows the value of the attribute, which is still wrong since &#163;5 is the selling price:</p><pre><code>Calling Price.__set__()
Calling Price.__set__()
Calling Price.__get__()
5</code></pre><p>We&#8217;ll soon stop the selling price from overwriting the cost price.</p><p>But first, let&#8217;s check that the validation logic works:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ImmM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0819a9d2-4caf-42f0-8f6d-07ea94eac59a_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!ImmM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0819a9d2-4caf-42f0-8f6d-07ea94eac59a_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!ImmM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0819a9d2-4caf-42f0-8f6d-07ea94eac59a_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!ImmM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0819a9d2-4caf-42f0-8f6d-07ea94eac59a_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ImmM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0819a9d2-4caf-42f0-8f6d-07ea94eac59a_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0819a9d2-4caf-42f0-8f6d-07ea94eac59a_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12615,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0819a9d2-4caf-42f0-8f6d-07ea94eac59a_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ImmM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0819a9d2-4caf-42f0-8f6d-07ea94eac59a_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!ImmM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0819a9d2-4caf-42f0-8f6d-07ea94eac59a_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!ImmM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0819a9d2-4caf-42f0-8f6d-07ea94eac59a_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!ImmM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0819a9d2-4caf-42f0-8f6d-07ea94eac59a_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#36</figcaption></figure></div><p>This raises an error:</p><pre><code>Traceback (most recent call last):
  ... in &lt;module&gt;
    coffee = Product(&#8221;Espresso&#8221;, -3.2, 5)
  ... in Product.__init__
    self.cost_price = cost_price
    ^^^^^^^^^^^^^^^
  ... in Price.__set__
    raise ValueError(f&#8221;&#8217;cost_price&#8217; can&#8217;t be less than zero&#8221;)
    
ValueError: &#8216;cost_price&#8217; can&#8217;t be less than zero</code></pre><p>And so does this:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KJ1F!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefd3b93e-85a6-4507-a760-15edfb649574_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!KJ1F!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefd3b93e-85a6-4507-a760-15edfb649574_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!KJ1F!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefd3b93e-85a6-4507-a760-15edfb649574_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!KJ1F!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefd3b93e-85a6-4507-a760-15edfb649574_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!KJ1F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefd3b93e-85a6-4507-a760-15edfb649574_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/efd3b93e-85a6-4507-a760-15edfb649574_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12876,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefd3b93e-85a6-4507-a760-15edfb649574_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!KJ1F!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefd3b93e-85a6-4507-a760-15edfb649574_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!KJ1F!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefd3b93e-85a6-4507-a760-15edfb649574_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!KJ1F!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefd3b93e-85a6-4507-a760-15edfb649574_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!KJ1F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefd3b93e-85a6-4507-a760-15edfb649574_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#37</figcaption></figure></div><p>This time, the error message is different:</p><pre><code>...
TypeError: Invalid format for &#8216;cost_price&#8217;: three point two</code></pre><p>Another minor note for completeness&#8217;s sake. We mentioned that you can call the descriptor through the class, such as <code>Product.cost_price</code>. Since <code>instance</code> is <code>None</code> in this case, the code in <code>.__get__()</code> raises an error since <code>None</code> doesn&#8217;t have a <code>._cost_price</code> attribute (or any attribute, for that matter!) To account for this, you can add the following code in <code>.__get__()</code>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ti0g!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9807d897-33e8-4e66-b365-0205723e2cc6_1200x504.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ti0g!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9807d897-33e8-4e66-b365-0205723e2cc6_1200x504.png 424w, https://substackcdn.com/image/fetch/$s_!Ti0g!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9807d897-33e8-4e66-b365-0205723e2cc6_1200x504.png 848w, https://substackcdn.com/image/fetch/$s_!Ti0g!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9807d897-33e8-4e66-b365-0205723e2cc6_1200x504.png 1272w, https://substackcdn.com/image/fetch/$s_!Ti0g!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9807d897-33e8-4e66-b365-0205723e2cc6_1200x504.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ti0g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9807d897-33e8-4e66-b365-0205723e2cc6_1200x504.png" width="1200" height="504" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9807d897-33e8-4e66-b365-0205723e2cc6_1200x504.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:504,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:52650,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9807d897-33e8-4e66-b365-0205723e2cc6_1200x504.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ti0g!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9807d897-33e8-4e66-b365-0205723e2cc6_1200x504.png 424w, https://substackcdn.com/image/fetch/$s_!Ti0g!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9807d897-33e8-4e66-b365-0205723e2cc6_1200x504.png 848w, https://substackcdn.com/image/fetch/$s_!Ti0g!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9807d897-33e8-4e66-b365-0205723e2cc6_1200x504.png 1272w, https://substackcdn.com/image/fetch/$s_!Ti0g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9807d897-33e8-4e66-b365-0205723e2cc6_1200x504.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#38</figcaption></figure></div><p>If you call the descriptor from a class, you just get the descriptor back. There&#8217;s no instance, so there&#8217;s no value to fetch in this case! But let&#8217;s move on&#8230;</p><div><hr></div><p><em>Your call&#8230;</em></p><p><em>The Python Coding Place offers something for everyone:</em></p><p><em>&#8226; a super-personalised one-to-one 6-month mentoring option</em><br><em>$ 4,750</em></p><p><em>&#8226; individual one-to-one sessions</em><br><em>$ 125</em></p><p><em>&#8226; a self-led route with access to 60+ hrs of exceptional video courses and a support forum</em><br><em>$ 400</em></p><p><em><a href="https://thepythoncodingplace.com?utm_source=the-python-coding-stack">Which The Python Coding Place student are you?</a></em></p><div><hr></div><h3><strong>Making a general descriptor class</strong></h3><p>This works. But it only works for the <code>.cost_price</code> attribute since there are a few references to this attribute hard-coded in the <code>.__get__()</code> and <code>.__set__()</code> methods:</p><ol><li><p>You use the string <code>"_cost_price"</code> when calling <code>getattr()</code> and <code>setattr()</code>.</p></li><li><p>You also refer to the attribute name <code>'cost_price'</code> in the error messages.</p></li></ol><p>Hard-coding the cost price attribute names also has the undesirable effect of overwriting the cost price with the selling price!</p><p>Ideally, you&#8217;d like to access the public attribute name <code>"cost_price"</code> (the name of the descriptor) and the non-public data attribute name <code>"_cost_price"</code> dynamically. Recall that these are attributes in the <code>Product</code> class. Python passes the descriptor object <code>self</code> to the <code>.__get__</code> and <code>.__set__()</code> methods, but not its attribute name within <code>Product</code>.</p><p>But descriptors can also deal with this problem. A descriptor allows you to pass the attribute name from the <code>Product</code> class to the <code>Price</code> class using <code>.__set_name__()</code> in the descriptor class:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1bWl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e2a241a-00c7-4146-aece-28890f77304c_1200x546.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1bWl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e2a241a-00c7-4146-aece-28890f77304c_1200x546.png 424w, https://substackcdn.com/image/fetch/$s_!1bWl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e2a241a-00c7-4146-aece-28890f77304c_1200x546.png 848w, https://substackcdn.com/image/fetch/$s_!1bWl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e2a241a-00c7-4146-aece-28890f77304c_1200x546.png 1272w, https://substackcdn.com/image/fetch/$s_!1bWl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e2a241a-00c7-4146-aece-28890f77304c_1200x546.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!1bWl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e2a241a-00c7-4146-aece-28890f77304c_1200x546.png" width="1200" height="546" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1e2a241a-00c7-4146-aece-28890f77304c_1200x546.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:546,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:51078,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e2a241a-00c7-4146-aece-28890f77304c_1200x546.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1bWl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e2a241a-00c7-4146-aece-28890f77304c_1200x546.png 424w, https://substackcdn.com/image/fetch/$s_!1bWl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e2a241a-00c7-4146-aece-28890f77304c_1200x546.png 848w, https://substackcdn.com/image/fetch/$s_!1bWl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e2a241a-00c7-4146-aece-28890f77304c_1200x546.png 1272w, https://substackcdn.com/image/fetch/$s_!1bWl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e2a241a-00c7-4146-aece-28890f77304c_1200x546.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#39</figcaption></figure></div><p>I&#8217;ve put similar <code>print()</code> calls to the ones you used earlier to help us identify what&#8217;s what. Remove the code that creates the instance <code>coffee</code>, so that your module only defines the two classes, <code>Price</code> and <code>Product</code>, and nothing else. Run your script, and you&#8217;ll see this output:</p><pre><code>Calling Price.__set_name__()
self=&lt;__main__.Price object at 0x101bbcec0&gt;
owner=&lt;class &#8216;__main__.Product&#8217;&gt;
name=&#8217;cost_price&#8217;

Calling Price.__set_name__()
self=&lt;__main__.Price object at 0x101bacf50&gt;
owner=&lt;class &#8216;__main__.Product&#8217;&gt;
name=&#8217;selling_price&#8217;</code></pre><p>Python calls <code>Price.__set_name__</code> when you create the descriptors within <code>Product</code>. Recall that you create the descriptors when you assign them to the class attributes. These attributes, and therefore the descriptors, are created when you define the <code>Product</code> class and not when you create <code>Product</code> instances.</p><p>As before, <code>self</code> refers to the descriptor object itself. The <code>owner</code> parameter still refers to the class in which the descriptors are defined, <code>Product</code> in this example.</p><p>Finally, <code>name</code> is a string containing the attribute name you use within the <code>Product</code> class. Therefore, <code>name</code> is either <code>"cost_price"</code> or <code>"selling_price"</code>.</p><p>You can now use this name within code in the <code>Price</code> class. First, let&#8217;s tidy up the <code>.__set_name__()</code> method:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!d5gl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5597bae-6a07-4739-8dbb-b2f404507e4d_1200x504.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!d5gl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5597bae-6a07-4739-8dbb-b2f404507e4d_1200x504.png 424w, https://substackcdn.com/image/fetch/$s_!d5gl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5597bae-6a07-4739-8dbb-b2f404507e4d_1200x504.png 848w, https://substackcdn.com/image/fetch/$s_!d5gl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5597bae-6a07-4739-8dbb-b2f404507e4d_1200x504.png 1272w, https://substackcdn.com/image/fetch/$s_!d5gl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5597bae-6a07-4739-8dbb-b2f404507e4d_1200x504.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!d5gl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5597bae-6a07-4739-8dbb-b2f404507e4d_1200x504.png" width="1200" height="504" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a5597bae-6a07-4739-8dbb-b2f404507e4d_1200x504.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:504,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:46602,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5597bae-6a07-4739-8dbb-b2f404507e4d_1200x504.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!d5gl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5597bae-6a07-4739-8dbb-b2f404507e4d_1200x504.png 424w, https://substackcdn.com/image/fetch/$s_!d5gl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5597bae-6a07-4739-8dbb-b2f404507e4d_1200x504.png 848w, https://substackcdn.com/image/fetch/$s_!d5gl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5597bae-6a07-4739-8dbb-b2f404507e4d_1200x504.png 1272w, https://substackcdn.com/image/fetch/$s_!d5gl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5597bae-6a07-4739-8dbb-b2f404507e4d_1200x504.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#40</figcaption></figure></div><p>You create two instance attributes in the <code>Price</code> object to refer to the private and public names. You may recall that I mentioned Python prefers the term &#8220;non-public&#8221; over &#8220;private&#8221;. However, the Python documentation also occasionally uses the term &#8220;private&#8221;, including when discussing descriptors. It&#8217;s common to see <code>.private_name</code> used as the instance attribute within a descriptor. I&#8217;ll acquiesce.</p><p>Now, you can update the rest of the code:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XBTo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440fb805-c9c2-49f2-8a46-b584108a4952_1290x1302.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XBTo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440fb805-c9c2-49f2-8a46-b584108a4952_1290x1302.png 424w, https://substackcdn.com/image/fetch/$s_!XBTo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440fb805-c9c2-49f2-8a46-b584108a4952_1290x1302.png 848w, https://substackcdn.com/image/fetch/$s_!XBTo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440fb805-c9c2-49f2-8a46-b584108a4952_1290x1302.png 1272w, https://substackcdn.com/image/fetch/$s_!XBTo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440fb805-c9c2-49f2-8a46-b584108a4952_1290x1302.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!XBTo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440fb805-c9c2-49f2-8a46-b584108a4952_1290x1302.png" width="1290" height="1302" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/440fb805-c9c2-49f2-8a46-b584108a4952_1290x1302.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1302,&quot;width&quot;:1290,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:137424,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440fb805-c9c2-49f2-8a46-b584108a4952_1290x1302.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XBTo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440fb805-c9c2-49f2-8a46-b584108a4952_1290x1302.png 424w, https://substackcdn.com/image/fetch/$s_!XBTo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440fb805-c9c2-49f2-8a46-b584108a4952_1290x1302.png 848w, https://substackcdn.com/image/fetch/$s_!XBTo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440fb805-c9c2-49f2-8a46-b584108a4952_1290x1302.png 1272w, https://substackcdn.com/image/fetch/$s_!XBTo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440fb805-c9c2-49f2-8a46-b584108a4952_1290x1302.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#41</figcaption></figure></div><p>You use <code>self.private_name</code> as an argument in <code>getattr()</code> and <code>setattr()</code> instead of the hard-coded string you had before. And you use <code>self.public_name</code> in the error messages. Note that I&#8217;ve also removed the breadcrumb calls to <code>print()</code> since we no longer need to show when Python calls each of these methods.</p><p>Let&#8217;s test:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ijMQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5e22f46-7cdb-4fb3-9922-1a909fdbebf4_1200x336.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ijMQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5e22f46-7cdb-4fb3-9922-1a909fdbebf4_1200x336.png 424w, https://substackcdn.com/image/fetch/$s_!ijMQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5e22f46-7cdb-4fb3-9922-1a909fdbebf4_1200x336.png 848w, https://substackcdn.com/image/fetch/$s_!ijMQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5e22f46-7cdb-4fb3-9922-1a909fdbebf4_1200x336.png 1272w, https://substackcdn.com/image/fetch/$s_!ijMQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5e22f46-7cdb-4fb3-9922-1a909fdbebf4_1200x336.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ijMQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5e22f46-7cdb-4fb3-9922-1a909fdbebf4_1200x336.png" width="1200" height="336" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d5e22f46-7cdb-4fb3-9922-1a909fdbebf4_1200x336.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:336,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:32952,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5e22f46-7cdb-4fb3-9922-1a909fdbebf4_1200x336.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ijMQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5e22f46-7cdb-4fb3-9922-1a909fdbebf4_1200x336.png 424w, https://substackcdn.com/image/fetch/$s_!ijMQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5e22f46-7cdb-4fb3-9922-1a909fdbebf4_1200x336.png 848w, https://substackcdn.com/image/fetch/$s_!ijMQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5e22f46-7cdb-4fb3-9922-1a909fdbebf4_1200x336.png 1272w, https://substackcdn.com/image/fetch/$s_!ijMQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5e22f46-7cdb-4fb3-9922-1a909fdbebf4_1200x336.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#42</figcaption></figure></div><p>You can set and get values, including within the <code>.profit_margin</code> property:</p><pre><code>3.2
5
1.8</code></pre><p>But you can&#8217;t set invalid values. Try <code>coffee.cost_price = -2</code> and <code>coffee.cost_price = "three point two"</code> to confirm the validation code works.</p><p>Here&#8217;s the full code containing both classes:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mabD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52442f70-58c8-4951-a3f5-e913d607c812_1290x1764.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mabD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52442f70-58c8-4951-a3f5-e913d607c812_1290x1764.png 424w, https://substackcdn.com/image/fetch/$s_!mabD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52442f70-58c8-4951-a3f5-e913d607c812_1290x1764.png 848w, https://substackcdn.com/image/fetch/$s_!mabD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52442f70-58c8-4951-a3f5-e913d607c812_1290x1764.png 1272w, https://substackcdn.com/image/fetch/$s_!mabD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52442f70-58c8-4951-a3f5-e913d607c812_1290x1764.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!mabD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52442f70-58c8-4951-a3f5-e913d607c812_1290x1764.png" width="1290" height="1764" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/52442f70-58c8-4951-a3f5-e913d607c812_1290x1764.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1764,&quot;width&quot;:1290,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:197505,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52442f70-58c8-4951-a3f5-e913d607c812_1290x1764.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mabD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52442f70-58c8-4951-a3f5-e913d607c812_1290x1764.png 424w, https://substackcdn.com/image/fetch/$s_!mabD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52442f70-58c8-4951-a3f5-e913d607c812_1290x1764.png 848w, https://substackcdn.com/image/fetch/$s_!mabD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52442f70-58c8-4951-a3f5-e913d607c812_1290x1764.png 1272w, https://substackcdn.com/image/fetch/$s_!mabD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52442f70-58c8-4951-a3f5-e913d607c812_1290x1764.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#43</figcaption></figure></div><h2><strong>How About Properties, Then?</strong></h2><p>You use a property for the <code>.profit_margin</code> attribute in <code>Product</code>. This is a simple read-only attribute you use to calculate the value each time you need the attribute. You could use properties for <code>.selling_price</code> and <code>.cost_price</code>, but there&#8217;s plenty of boilerplate code needed for each attribute. Annoying, especially if you need many price-related attributes in your class, not just cost and selling prices.</p><p>Descriptors allow you to put the validation logic into a separate class. And descriptors aren&#8217;t restricted to data validation. You can use descriptors whenever you need to execute any logic each time you get or set the value of an attribute. And you can use the <code>Price</code> descriptor elsewhere, too. It&#8217;s not restricted to the <code>Product</code> class.</p><p>But remember the <code>property</code> object? This is itself a descriptor. Here&#8217;s evidence of this:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!u0kr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e938d33-156a-40be-b6b2-964b1b991242_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!u0kr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e938d33-156a-40be-b6b2-964b1b991242_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!u0kr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e938d33-156a-40be-b6b2-964b1b991242_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!u0kr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e938d33-156a-40be-b6b2-964b1b991242_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!u0kr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e938d33-156a-40be-b6b2-964b1b991242_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7e938d33-156a-40be-b6b2-964b1b991242_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:14458,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e938d33-156a-40be-b6b2-964b1b991242_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!u0kr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e938d33-156a-40be-b6b2-964b1b991242_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!u0kr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e938d33-156a-40be-b6b2-964b1b991242_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!u0kr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e938d33-156a-40be-b6b2-964b1b991242_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!u0kr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e938d33-156a-40be-b6b2-964b1b991242_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#44</figcaption></figure></div><p>This code returns <code>True</code>. The <code>.profit_margin</code> attribute is a property. It has the <code>.__get__()</code> special method, which is sufficient to make it a descriptor. A property is a descriptor.</p><p>Note that you&#8217;re accessing this property through the class name within <code>hasattr()</code>. Remember the code checking whether <code>instance</code> is <code>None</code> in <code>.__get__()</code>? You used the <code>if instance is None:</code> check to return the descriptor itself when you call the descriptor from a class. That&#8217;s why you can introspect the descriptor through <code>Product.profit_margin</code>. But don&#8217;t worry too much about this detail!</p><h2><strong>Final Words</strong></h2><p>How often have you used a method, such as in this case?</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LbA3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae907ea5-2f57-4220-9fbb-c08605c2741a_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!LbA3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae907ea5-2f57-4220-9fbb-c08605c2741a_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!LbA3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae907ea5-2f57-4220-9fbb-c08605c2741a_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!LbA3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae907ea5-2f57-4220-9fbb-c08605c2741a_1200x294.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!LbA3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae907ea5-2f57-4220-9fbb-c08605c2741a_1200x294.png" width="1200" height="294" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ae907ea5-2f57-4220-9fbb-c08605c2741a_1200x294.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:294,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:20242,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae907ea5-2f57-4220-9fbb-c08605c2741a_1200x294.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LbA3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae907ea5-2f57-4220-9fbb-c08605c2741a_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!LbA3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae907ea5-2f57-4220-9fbb-c08605c2741a_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!LbA3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae907ea5-2f57-4220-9fbb-c08605c2741a_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!LbA3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae907ea5-2f57-4220-9fbb-c08605c2741a_1200x294.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#45</figcaption></figure></div><p>Millions of times, you say? You may already be aware that this call to the <code>.append()</code> method is similar to this more verbose version:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eXXC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a3ecf56-857f-4bf9-a23b-225ce9e27d0c_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!eXXC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a3ecf56-857f-4bf9-a23b-225ce9e27d0c_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!eXXC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a3ecf56-857f-4bf9-a23b-225ce9e27d0c_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!eXXC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a3ecf56-857f-4bf9-a23b-225ce9e27d0c_1200x294.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!eXXC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a3ecf56-857f-4bf9-a23b-225ce9e27d0c_1200x294.png" width="1200" height="294" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0a3ecf56-857f-4bf9-a23b-225ce9e27d0c_1200x294.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:294,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:21512,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a3ecf56-857f-4bf9-a23b-225ce9e27d0c_1200x294.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eXXC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a3ecf56-857f-4bf9-a23b-225ce9e27d0c_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!eXXC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a3ecf56-857f-4bf9-a23b-225ce9e27d0c_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!eXXC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a3ecf56-857f-4bf9-a23b-225ce9e27d0c_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!eXXC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a3ecf56-857f-4bf9-a23b-225ce9e27d0c_1200x294.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#46</figcaption></figure></div><p>In this version, you use <code>append()</code> as a function. It&#8217;s a function that&#8217;s an attribute of the <code>list</code> class. Therefore, you pass the object as its first argument &#8211; this is the object that is assigned to the first parameter <code>self</code> in the <code>append()</code> signature.</p><p>Well, as it happens, Python uses the descriptor protocol to automatically equate <code>numbers.append(30)</code> to the function call <code>list.append(numbers, 30)</code>.</p><p>Functions are objects. And function objects are descriptors in Python. When you access them through an instance of a class, such as in <code>numbers.append(30)</code>, Python goes through the function&#8217;s <code>.__get__()</code> method and calls <code>list.append(numbers, 30)</code> so that you don&#8217;t have to worry about it:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!J-iy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e8c0159-a96d-4d22-9ab4-9eb4d5402dc2_1200x378.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!J-iy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e8c0159-a96d-4d22-9ab4-9eb4d5402dc2_1200x378.png 424w, https://substackcdn.com/image/fetch/$s_!J-iy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e8c0159-a96d-4d22-9ab4-9eb4d5402dc2_1200x378.png 848w, https://substackcdn.com/image/fetch/$s_!J-iy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e8c0159-a96d-4d22-9ab4-9eb4d5402dc2_1200x378.png 1272w, https://substackcdn.com/image/fetch/$s_!J-iy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e8c0159-a96d-4d22-9ab4-9eb4d5402dc2_1200x378.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!J-iy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e8c0159-a96d-4d22-9ab4-9eb4d5402dc2_1200x378.png" width="1200" height="378" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5e8c0159-a96d-4d22-9ab4-9eb4d5402dc2_1200x378.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:378,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:30787,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e8c0159-a96d-4d22-9ab4-9eb4d5402dc2_1200x378.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!J-iy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e8c0159-a96d-4d22-9ab4-9eb4d5402dc2_1200x378.png 424w, https://substackcdn.com/image/fetch/$s_!J-iy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e8c0159-a96d-4d22-9ab4-9eb4d5402dc2_1200x378.png 848w, https://substackcdn.com/image/fetch/$s_!J-iy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e8c0159-a96d-4d22-9ab4-9eb4d5402dc2_1200x378.png 1272w, https://substackcdn.com/image/fetch/$s_!J-iy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e8c0159-a96d-4d22-9ab4-9eb4d5402dc2_1200x378.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#47</figcaption></figure></div><p>Functions have a <code>.__get__()</code> method, which makes them descriptors. Since you can&#8217;t set the value for a function object, they don&#8217;t have a <code>.__set__()</code> method. Descriptors that only have a <code>.__get__()</code> special method, such as functions, are non-data descriptors. Non-data descriptors don&#8217;t get the same precedence as data descriptors when Python is figuring out what to do when you use the dot notation. But that&#8217;s a topic for another day&#8230;</p><p>As with many topics, learning about descriptors helps you learn about descriptors (duh!), but it also helps you understand Python better.</p><p>So, descriptors are a useful tool whenever you want to perform additional tasks, such as validation, whenever you get or set the value of an attribute. What makes them a bit weird until you get used to them is that the logic is defined in the descriptor class itself. The benefit of this is that you can use descriptors across many classes and rely on the same logic, without having to define that logic in each class.</p><p>For example, you can use <code>Price()</code> objects, which are descriptors, in any class where you need to use prices and apply the same validation you defined in this article. And you can add functions to <em>any</em> class safe in the knowledge that the descriptor protocol will ensure they&#8217;re treated as instance methods when they&#8217;re accessed (unless you use the <code>@classmethod</code> or <code>@staticmethod</code> decorators, which &#8211; you guessed it &#8211; also use the descriptor protocol to perform their magic.)</p><p>In summary, a descriptor is an object that hijacks the attribute access process triggered by the dot notation. Normally, when you access an attribute of an object, the object&#8217;s class deals with finding the value. However, when the attribute is a descriptor, the descriptor class takes over.</p><div><hr></div><p>Did you enjoy this article? Did you understand how descriptors work? That&#8217;s not an easy feat!</p><p>Do you want to become a premium subscriber and join <em>The Club</em> to help support this publication and ensure there are more articles like this one?</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ohw0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F715dbc5d-d1fa-4ff2-91f0-d41aa7d85dc3_4288x2848.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ohw0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F715dbc5d-d1fa-4ff2-91f0-d41aa7d85dc3_4288x2848.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Ohw0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F715dbc5d-d1fa-4ff2-91f0-d41aa7d85dc3_4288x2848.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Ohw0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F715dbc5d-d1fa-4ff2-91f0-d41aa7d85dc3_4288x2848.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Ohw0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F715dbc5d-d1fa-4ff2-91f0-d41aa7d85dc3_4288x2848.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ohw0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F715dbc5d-d1fa-4ff2-91f0-d41aa7d85dc3_4288x2848.jpeg" width="622" height="413.10027472527474" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/715dbc5d-d1fa-4ff2-91f0-d41aa7d85dc3_4288x2848.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:967,&quot;width&quot;:1456,&quot;resizeWidth&quot;:622,&quot;bytes&quot;:796911,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/191055116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F715dbc5d-d1fa-4ff2-91f0-d41aa7d85dc3_4288x2848.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ohw0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F715dbc5d-d1fa-4ff2-91f0-d41aa7d85dc3_4288x2848.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Ohw0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F715dbc5d-d1fa-4ff2-91f0-d41aa7d85dc3_4288x2848.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Ohw0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F715dbc5d-d1fa-4ff2-91f0-d41aa7d85dc3_4288x2848.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Ohw0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F715dbc5d-d1fa-4ff2-91f0-d41aa7d85dc3_4288x2848.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em><a href="https://www.pexels.com/photo/round-clock-mechanism-4760575/">Photo by Ej Agumbay</a></em></p><div><hr></div><p><em>Code in this article uses Python 3.14</em></p><p><em>The code images used in this article are created using <a href="https://snappify.cello.so/f4AsFrwgwov">Snappify</a>.</em> <em>[Affiliate link]</em></p><div><hr></div><p><em>For more Python resources, you can also visit</em> <em><a href="https://realpython.com?utm_source=the-python-coding-stack">Real Python</a>&#8212;you may even stumble on one of my own articles or courses there!</em></p><p><em>Also, are you interested in technical writing? You&#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at</em> <em><a href="http://stephengruppetta.com/breaking-the-rules">Breaking the Rules</a></em>.</p><p><em>And you can find out more about me at</em> <em><a href="https://stephengruppetta.com?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p><div><hr></div><p>Further reading related to this article&#8217;s topic:</p><ul><li><p><a href="https://www.thepythoncodingstack.com/p/the-properties-of-python-property">The Properties of Python&#8217;s </a><code>property</code></p></li></ul><div><hr></div><h2>Appendix: Code Blocks</h2><h5>Code Block #1</h5><pre><code><code>class Product:
    def __init__(self, name, cost_price, selling_price):
        self.name = name
        self.cost_price = cost_price
        self.selling_price = selling_price
        self.profit_margin = self.selling_price - self.cost_price
</code></code></pre><h5>Code Block #2</h5><pre><code><code># ...
coffee = Product("Espresso", 2.5, 4.5)
print(f"{coffee.profit_margin=}")
</code></code></pre><h5>Code Block #3</h5><pre><code><code># ...
coffee = Product("Espresso", 2.5, 4.5)
coffee.selling_price = 5.5
print(f"{coffee.profit_margin=}")
</code></code></pre><h5>Code Block #4</h5><pre><code><code>class Product:
    def __init__(self, name, cost_price, selling_price):
        self.name = name
        self.cost_price = cost_price
        self.selling_price = selling_price

    @property
    def profit_margin(self):
        return self.selling_price - self.cost_price

coffee = Product("Espresso", 2.5, 4.5)
coffee.selling_price = 5.5
print(f"{coffee.profit_margin=}")
</code></code></pre><h5>Code Block #5</h5><pre><code><code># ...
coffee.profit_margin = 4
</code></code></pre><h5>Code Block #6</h5><pre><code><code>class Product:
    def __init__(self, name, cost_price, selling_price):
        self.name = name
        if cost_price &lt; 0:
            raise ValueError("Cost price cannot be negative")
        self.cost_price = cost_price
        self.selling_price = selling_price

    @property
    def profit_margin(self):
        return self.selling_price - self.cost_price
      
# ...
</code></code></pre><h5>Code Block #7</h5><pre><code><code># ...
coffee = Product("Espresso", -2.5, 4.5)
</code></code></pre><h5>Code Block #8</h5><pre><code><code># ...
coffee = Product("Espresso", 2.5, 4.5)  # Back to correct cost price
coffee.cost_price = -1
print(f"{coffee.cost_price=}")
</code></code></pre><h5>Code Block #9</h5><pre><code><code>class Product:
    def __init__(self, name, cost_price, selling_price):
        self.name = name
        self.selling_price = selling_price
        
    @property
    def cost_price(self):
        # ???
        ...

    @property
    def profit_margin(self):
        return self.selling_price - self.cost_price
      
# ...
</code></code></pre><h5>Code Block #10</h5><pre><code><code>class Product:
    def __init__(self, name, cost_price, selling_price):
        self.name = name
        self._cost_price = cost_price
        self.selling_price = selling_price

    @property
    def cost_price(self):
        return self._cost_price

    @property
    def profit_margin(self):
        return self.selling_price - self.cost_price
      
# ...
</code></code></pre><h5>Code Block #11</h5><pre><code><code># ...
coffee = Product("Espresso", 2.5, 4.5)
print(f"{coffee.cost_price=}")
</code></code></pre><h5>Code Block #12</h5><pre><code><code># ...
coffee = Product("Espresso", 2.5, 4.5)
coffee.cost_price = 3
print(f"{coffee.cost_price=}")
</code></code></pre><h5>Code Block #13</h5><pre><code><code>class Product:
    def __init__(self, name, cost_price, selling_price):
        self.name = name
        self._cost_price = cost_price
        self.selling_price = selling_price

    @property
    def cost_price(self):
        return self._cost_price

    @cost_price.setter
    def cost_price(self, value):
        if value &lt; 0:
            raise ValueError("Cost price cannot be negative")
        self._cost_price = value

    @property
    def profit_margin(self):
        return self.selling_price - self.cost_price

coffee = Product("Espresso", 2.5, 4.5)
coffee.cost_price = 3
print(f"{coffee.cost_price=}")
</code></code></pre><h5>Code Block #14</h5><pre><code><code># ...
coffee = Product("Espresso", 2.5, 4.5)
coffee.cost_price = -3
print(f"{coffee.cost_price=}")
</code></code></pre><h5>Code Block #15</h5><pre><code><code># ...
coffee = Product("Espresso", -2.5, 4.5)
print(f"{coffee.cost_price=}")
</code></code></pre><h5>Code Block #16</h5><pre><code><code>class Product:
    def __init__(self, name, cost_price, selling_price):
        self.name = name
        self.cost_price = cost_price
        self.selling_price = selling_price

    @property
    def cost_price(self):
        return self._cost_price

    @cost_price.setter
    def cost_price(self, value):
        if value &lt; 0:
            raise ValueError("Cost price cannot be negative")
        self._cost_price = value

    @property
    def profit_margin(self):
        return self.selling_price - self.cost_price
      
# ...
</code></code></pre><h5>Code Block #17</h5><pre><code><code>0.1 + 0.2
# 0.30000000000000004
</code></code></pre><h5>Code Block #18</h5><pre><code><code>from decimal import Decimal
Decimal("0.1") + Decimal("0.2")
# Decimal('0.3')
</code></code></pre><h5>Code Block #19</h5><pre><code><code>from decimal import Decimal, InvalidOperation

class Product:
    def __init__(self, name, cost_price, selling_price):
        self.name = name
        self.cost_price = cost_price
        self.selling_price = selling_price

    @property
    def cost_price(self):
        return self._cost_price

    @cost_price.setter
    def cost_price(self, value):
        try:
            value = Decimal(str(value))
        except InvalidOperation:
            raise TypeError("Invalid format for cost price")
        if value &lt; 0:
            raise ValueError("Cost price cannot be negative")
        self._cost_price = value

    @property
    def profit_margin(self):
        return self.selling_price - self.cost_price
      
# ...
</code></code></pre><h5>Code Block #20</h5><pre><code><code>from decimal import Decimal, InvalidOperation

class Product:
    def __init__(self, name, cost_price, selling_price):
        self.name = name
        self.cost_price = cost_price
        self.selling_price = selling_price

    @property
    def cost_price(self):
        return self._cost_price

    @cost_price.setter
    def cost_price(self, value):
        try:
            value = Decimal(str(value))
        except InvalidOperation:
            raise TypeError("Invalid format for cost price")
        if value &lt; 0:
            raise ValueError("Cost price cannot be negative")
        self._cost_price = value

    @property
    def selling_price(self):
        return self._selling_price

    @selling_price.setter
    def selling_price(self, value):
        try:
            value = Decimal(str(value))
        except InvalidOperation:
            raise TypeError("Invalid format for selling price")
        if value &lt; 0:
            raise ValueError("Selling price cannot be negative")
        self._selling_price = value

    @property
    def profit_margin(self):
        return self.selling_price - self.cost_price
</code></code></pre><h5>Code Block #21</h5><pre><code><code># ...

class Price:
    """Descriptor to handle price validation and Decimal conversion."""
    
    def __get__(self, instance, owner):
        ...

    def __set__(self, instance, value):
        ...
        
# ...
</code></code></pre><h5>Code Block #22</h5><pre><code><code># ...

class Product:
    cost_price = Price()
    selling_price = Price()

    def __init__(self, name, cost_price, selling_price):
        self.name = name
        self.cost_price = cost_price
        self.selling_price = selling_price

    @property
    def profit_margin(self):
        return self.selling_price - self.cost_price

# ...
</code></code></pre><h5>Code Block #23</h5><pre><code><code># ...

class Price:
    def __get__(self, instance, owner):
        print("Calling Price.__get__()")
        return "Price coming soon..."

    def __set__(self, instance, value):
        print("Calling Price.__set__()")

# ...
</code></code></pre><h5>Code Block #24</h5><pre><code><code>class Price:
    def __get__(self, instance, owner):
        print("Calling Price.__get__()")
        return "Price coming soon..."

    def __set__(self, instance, value):
        print("Calling Price.__set__()")

class Product:
    cost_price = Price()
    selling_price = Price()

    def __init__(self, name, cost_price, selling_price):
        self.name = name
        self.cost_price = cost_price
        self.selling_price = selling_price

    @property
    def profit_margin(self):
        return self.selling_price - self.cost_price


coffee = Product("Espresso", 3.2, 5)
</code></code></pre><h5>Code Block #25</h5><pre><code><code># ...
coffee = Product("Espresso", 3.2, 5)
print(coffee.selling_price)
</code></code></pre><h5>Code Block #26</h5><pre><code><code>class Price:
    def __get__(self, instance, owner):
        print("Calling Price.__get__()")
        print(f"{self=}")
        print(f"{instance=}")
        print(f"{owner=}")
        return "Price coming soon..."
      
      # ...
</code></code></pre><h5>Code Block #27</h5><pre><code><code>coffee.selling_price
</code></code></pre><h5>Code Block #28</h5><pre><code><code># ...
print(Product.selling_price)
</code></code></pre><h5>Code Block #29</h5><pre><code><code>class Price:
    # ...

    def __set__(self, instance, value):
        print("Calling Price.__set__()")
        print(f"{self=}")
        print(f"{instance=}")
        print(f"{value=}")
# ...
</code></code></pre><h5>Code Block #30</h5><pre><code><code># ...
coffee = Product("Espresso", 3.2, 5)
</code></code></pre><h5>Code Block #31</h5><pre><code><code>from decimal import Decimal, InvalidOperation

class Price:
    def __get__(self, instance, owner):
        print("Calling Price.__get__()")
        # more code coming soon here

    def __set__(self, instance, value):
        print("Calling Price.__set__()")
        try:
            value = Decimal(str(value))
        except InvalidOperation:
            raise TypeError(f"Invalid format for 'cost_price': {value}")

        if value &lt; 0:
            raise ValueError(f"'cost_price' can't be less than zero")

        setattr(instance, "_cost_price", value)

# ...
</code></code></pre><h5>Code Block #32</h5><pre><code><code># ...

coffee = Product("Espresso", 3.2, 5)
print(coffee.cost_price)
</code></code></pre><h5>Code Block #33</h5><pre><code><code># ...

coffee = Product("Espresso", 3.2, 5)
print(coffee._cost_price)
</code></code></pre><h5>Code Block #34</h5><pre><code><code>from decimal import Decimal, InvalidOperation

class Price:
    def __get__(self, instance, owner):
        print("Calling Price.__get__()")
        return getattr(instance, "_cost_price")

    def __set__(self, instance, value):
        print("Calling Price.__set__()")
        try:
            value = Decimal(str(value))
        except InvalidOperation:
            raise TypeError(f"Invalid format for 'cost_price': {value}")

        if value &lt; 0:
            raise ValueError(f"'cost_price' can't be less than zero")

        setattr(instance, "_cost_price", value)
</code></code></pre><h5>Code Block #35</h5><pre><code><code># ...

coffee = Product("Espresso", 3.2, 5)
print(coffee.cost_price)
</code></code></pre><h5>Code Block #36</h5><pre><code><code># ...

coffee = Product("Espresso", -3.2, 5)
</code></code></pre><h5>Code Block #37</h5><pre><code><code># ...
coffee = Product("Espresso", "three point two", 5)
</code></code></pre><h5>Code Block #38</h5><pre><code><code>from decimal import Decimal, InvalidOperation

class Price:
    def __get__(self, instance, owner):
        print("Calling Price.__get__()")
        if instance is None:
            return self
        return getattr(instance, "_cost_price")
    # ...
</code></code></pre><h5>Code Block #39</h5><pre><code><code>from decimal import Decimal, InvalidOperation

class Price:
    def __set_name__(self, owner, name):
        print("Calling Price.__set_name__()")
        print(f"{self=}")
        print(f"{owner=}")
        print(f"{name=}")
        
&#9;&#9;# ...
</code></code></pre><h5>Code Block #40</h5><pre><code><code>from decimal import Decimal, InvalidOperation

class Price:
    def __set_name__(self, owner, name):
        print("Calling Price.__set_name__()")
        self.public_name = name
        self.private_name = f"_{name}"

# ...
</code></code></pre><h5>Code Block #41</h5><pre><code><code>from decimal import Decimal, InvalidOperation

class Price:
    def __set_name__(self, owner, name):
        self.public_name = name
        self.private_name = f"_{name}"

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return getattr(instance, self.private_name)

    def __set__(self, instance, value):
        try:
            value = Decimal(str(value))
        except InvalidOperation:
            raise TypeError(
                f"Invalid format for '{self.public_name}': {value}"
            )

        if value &lt; 0:
            raise ValueError(
                f"'{self.public_name}' can't be less than zero"
            )

        setattr(instance, self.private_name, value)

# ...
</code></code></pre><h5>Code Block #42</h5><pre><code><code># ...
coffee = Product("Espresso", 3.2, 5)
print(coffee.cost_price)
print(coffee.selling_price)
print(coffee.profit_margin)
</code></code></pre><h5>Code Block #43</h5><pre><code><code>from decimal import Decimal, InvalidOperation

class Price:
    def __set_name__(self, owner, name):
        self.public_name = name
        self.private_name = f"_{name}"

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return getattr(instance, self.private_name)

    def __set__(self, instance, value):
        try:
            value = Decimal(str(value))
        except InvalidOperation:
            raise TypeError(
                f"Invalid format for '{self.public_name}': {value}"
            )

        if value &lt; 0:
            raise ValueError(
                f"'{self.public_name}' can't be less than zero"
            )

        setattr(instance, self.private_name, value)

class Product:
    cost_price = Price()
    selling_price = Price()

    def __init__(self, name, cost_price, selling_price):
        self.name = name
        self.cost_price = cost_price
        self.selling_price = selling_price

    @property
    def profit_margin(self):
        return self.selling_price - self.cost_price
</code></code></pre><h5>Code Block #44</h5><pre><code><code># ...
print(hasattr(Product.profit_margin, "__get__"))
</code></code></pre><h5>Code Block #45</h5><pre><code><code>numbers = [10, 20]
numbers.append(30)
numbers
# [10, 20, 30]
</code></code></pre><h5>Code Block #46</h5><pre><code><code>numbers = [10, 20]
list.append(numbers, 30)
numbers
# [10, 20, 30]
</code></code></pre><h5>Code Block #47</h5><pre><code><code>def some_function():
    ...
hasattr(some_function, "__get__")
# True
hasattr(some_function, "__set__")
# False
</code></code></pre><div><hr></div><p><em><strong><a href="https://www.thepythoncodingstack.com/subscribe">Join</a></strong></em><strong><a href="https://www.thepythoncodingstack.com/subscribe"> The Club</a></strong><em>, the exclusive area for paid subscribers for <a href="https://www.thepythoncodingstack.com/s/the-club">more Python posts</a>, videos, a members&#8217; forum, and more.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><p><em>You can also support this publication by making a <a href="https://buy.stripe.com/00g3de2iGdgg4gg7su">one-off contribution of any amount you wish</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://buy.stripe.com/00g3de2iGdgg4gg7su&quot;,&quot;text&quot;:&quot;Support The Python Coding Stack&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://buy.stripe.com/00g3de2iGdgg4gg7su"><span>Support The Python Coding Stack</span></a></p><div><hr></div><p><em>For more Python resources, you can also visit</em> <em><a href="https://realpython.com?utm_source=the-python-coding-stack">Real Python</a>&#8212;you may even stumble on one of my own articles or courses there!</em></p><p><em>Also, are you interested in technical writing? You&#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at</em> <em><a href="http://stephengruppetta.com/breaking-the-rules">Breaking the Rules</a></em>.</p><p><em>And you can find out more about me at</em> <em><a href="https://stephengruppetta.com?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p>]]></content:encoded></item><item><title><![CDATA[Field Notes: First, Second, and Five Hundred and Twenty-Third]]></title><description><![CDATA[Have you ever needed to convert numbers like 22 into the string &#8220;twenty-two&#8221; or 523 into &#8220;five hundred and twenty-third&#8221;?]]></description><link>https://www.thepythoncodingstack.com/p/field-notes-first-second-and-five</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/field-notes-first-second-and-five</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Mon, 09 Mar 2026 15:25:48 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/4b146607-5d93-4c07-aed6-8b65fce4e239_840x600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Most of my posts on</em> The Python Coding Stack<em>, whether in the main publication or here in</em> The Club<em>, typically focus on some aspect of core Python and explore it through a step-by-step approach, a mini-project, or sometimes through an essay-type article.</em></p><p><em>But today, I&#8217;ll write a short post about some tools I came across that may be interesting. I&#8217;ll keep the post short since, if you&#8217;re interested, you can easily explore the packages independently &#8211; you won&#8217;t need my explanatory efforts.</em></p><div><hr></div><p>Remember some of the earliest code you ever wrote? It may have looked like this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;59aefe4d-d092-4ca5-8e83-abda31f26c1a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">first = input(&#8221;Enter the first number: &#8220;)
second = input(&#8221;Enter the second number: &#8220;)
print(
    f&#8221;The sum of the two numbers is {float(first) + float(second)}&#8221;
)</code></pre></div><p>Those days are long gone. But what if you wanted this code to work for any number of inputs, not just two?</p><p>I&#8217;m not talking about the code to work out the sum itself. You&#8217;d probably use a list to collect all the numbers and the <code>sum()</code> built-in function, or just use a running total. Whatever.</p><p>The annoying part is making user-friendly strings when asking for the input &#8211; it&#8217;s fine to write <code>"first"</code> and <code>"second"</code> when there are only two prompts &#8211; and then when showing the result, which currently says <code>"two numbers"</code>. But what if you have more?</p><p>I came across this need several times, but my solution has always been to change the string so that it&#8217;s general enough to work in all cases. I&#8217;m lazy, I know.</p><p>In any case, recently I decided to look for solutions. And of course, they exist! Everything seems to exist in the Python ecosystem. Several solutions&#8230;</p><h2><code>num2words</code></h2><p>The first solution is probably the simplest: the <code>num2words</code> package doesn&#8217;t do much beyond converting numbers to words, as its name suggests. You&#8217;ll need to install <code>num2words</code> using <code>pip</code>, <code>uv</code>, or whatever you use to install packages.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;5dc57e66-4a9b-46c9-bc1d-f2bac6e8a001&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">&gt;&gt;&gt; import num2words
&gt;&gt;&gt; num2words.num2words(34)
&#8216;thirty-four&#8217;</code></pre></div><p>The main function in the <code>num2words</code> module is also called <code>num2words()</code>, as often happens with such modules!</p><p>How far can it go?</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;6e551eb0-1a02-451d-852b-9cdcc1cb5ab6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">&gt;&gt;&gt; num2words.num2words(5468.23)
&#8216;five thousand, four hundred and sixty-eight point two three&#8217;</code></pre></div><p>Working in Spanish?</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;a36e2039-283d-4493-9031-fd0575d6a10a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">&gt;&gt;&gt; num2words.num2words(5468.23, lang=&#8221;es&#8221;)
&#8216;cinco mil cuatrocientos sesenta y ocho punto dos tres&#8217;</code></pre></div><p>Or Japanese?</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;f65faf64-470e-44cd-b50d-f536948c1b85&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">&gt;&gt;&gt; num2words.num2words(5468.23, lang=&#8221;ja&#8221;)
&#8216;&#20116;&#21315;&#22235;&#30334;&#20845;&#21313;&#20843;&#28857;&#20108;&#19977;&#8217;</code></pre></div>
      <p>
          <a href="https://www.thepythoncodingstack.com/p/field-notes-first-second-and-five">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[You Store Data and You Do Stuff With Data • The OOP Mindset]]></title><description><![CDATA[Why use classes and objects?]]></description><link>https://www.thepythoncodingstack.com/p/python-oop-mindset-you-store-data-and-you-do-stuff-with-data</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/python-oop-mindset-you-store-data-and-you-do-stuff-with-data</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Thu, 05 Mar 2026 22:58:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!X8ae!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Learning <em>how</em> to use a tool can be challenging. But learning <em>why</em> you should use that tool&#8211;and <em>when</em>&#8211;is sometimes even more challenging.</p><p>In this post, I&#8217;ll discuss the OOP mindset&#8211;or, let&#8217;s say, one way of viewing the object-oriented paradigm. I debated with myself whether to write this article here on <em>The Python Coding Stack</em>. Most articles here are aimed at the &#8220;intermediate-ish&#8221; Python programmer, whatever &#8220;intermediate&#8221; means. Most readers may feel they already <em>understand</em> the ethos and philosophy of OOP. I know I keep discovering new perspectives from time to time. So here&#8217;s a perspective I&#8217;ve been exploring in courses I ran recently.</p><p>As you can see, I decided to write this post. At worst, it serves as revision for some readers, perhaps a different route towards understanding why we (sometimes) define classes and (always) use objects in Python. And beginners read these articles, too!</p><p>If you feel you&#8217;re an OOP Pro, go ahead and skip this post. Or just read it anyway. It&#8217;s up to you. You can always catch up with anything you may have missed from <a href="https://www.thepythoncodingstack.com/archive">The Python Coding Stack&#8217;s Archive</a> instead &#8211; around 120 full-length articles and counting!</p><p><em>This post is inspired by the introduction to OOP in Chapter 7 in <a href="https://thepythoncodingplace.com/the-python-coding-book/">The Python Coding Book &#8211; The Relaxed and Friendly Programming Book for Beginners</a></em></p><div><hr></div><p>Meet Mark, a market seller. He sets up a stall in his local market and sells tea, coffee, and home-made biscuits (aka cookies for those in North America).</p><p>I&#8217;ll write two and a half versions of the code Mark could use to keep track of his stock and sales. The first one&#8211;that&#8217;s the half&#8211;is not very pretty. Don&#8217;t write code like this. But it will serve as a starting point for the main theme of this post. The second version is a stepping stone towards the third.</p><h2><strong>First (Half) Version &#8226; Not Pretty</strong></h2><p>Mark is also learning Python in his free time. He starts writing some code:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E89x!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5eac796e-cd41-4866-b921-657d182f9e06_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!E89x!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5eac796e-cd41-4866-b921-657d182f9e06_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!E89x!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5eac796e-cd41-4866-b921-657d182f9e06_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!E89x!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5eac796e-cd41-4866-b921-657d182f9e06_1200x294.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!E89x!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5eac796e-cd41-4866-b921-657d182f9e06_1200x294.png" width="1200" height="294" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5eac796e-cd41-4866-b921-657d182f9e06_1200x294.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:294,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:30093,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5eac796e-cd41-4866-b921-657d182f9e06_1200x294.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!E89x!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5eac796e-cd41-4866-b921-657d182f9e06_1200x294.png 424w, https://substackcdn.com/image/fetch/$s_!E89x!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5eac796e-cd41-4866-b921-657d182f9e06_1200x294.png 848w, https://substackcdn.com/image/fetch/$s_!E89x!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5eac796e-cd41-4866-b921-657d182f9e06_1200x294.png 1272w, https://substackcdn.com/image/fetch/$s_!E89x!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5eac796e-cd41-4866-b921-657d182f9e06_1200x294.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">All code blocks are available in text format at the end of this article &#8226; #1</figcaption></figure></div><p>He was planning to write some functions to update the stock, change the cost price and selling price, and deal with a sale from his market stall.</p><p>But he stopped.</p><p>We&#8217;ll also stop here with this version.</p><p>Mark is a Python beginner, but even he knew this was not the best way to store the relevant data for each product he sells.</p><p>Four separate objects? Not ideal. Mark knows that the data in these four lists are linked to each other. The first items in each list belong together, and so on. But he&#8217;ll need to manually ensure he maintains these links in the code he writes. Not impossible, but it&#8217;s asking for trouble. So many things can go wrong. And he&#8217;ll have a tough time writing the code, making all those links in every line he writes.</p><p>Mark knows what the link is between the various values. But the computer program doesn&#8217;t. The computer program sees four separate data structures, unrelated to each other. You can try writing an <code>update_stock()</code> function with this version to see the challenges, the manual work needed to connect data across separate structures.</p><p>Let&#8217;s move on.</p><h2><strong>Second Version &#8226; Dictionary and Functions</strong></h2><p>Mark learnt about dictionaries. They&#8217;re a great way to group data together:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!X257!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!X257!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png 424w, https://substackcdn.com/image/fetch/$s_!X257!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png 848w, https://substackcdn.com/image/fetch/$s_!X257!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png 1272w, https://substackcdn.com/image/fetch/$s_!X257!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!X257!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png" width="1200" height="840" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:840,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:63994,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!X257!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png 424w, https://substackcdn.com/image/fetch/$s_!X257!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png 848w, https://substackcdn.com/image/fetch/$s_!X257!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png 1272w, https://substackcdn.com/image/fetch/$s_!X257!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#2</figcaption></figure></div><p>Now there&#8217;s one data structure that contains the three products he sells. This structure contains three other data structures, each containing all the relevant data for each product.</p><p>The data are structured into these dictionaries to show what belongs where. In this version, the Python program &#8220;knows&#8221; which data items belong together. Each dictionary contains related values.</p><p>Well done, Mark! He understood the need to organise the data into a sensible structure. This is not the only combination of nested data structures Mark could use, but this is a valid option. Certainly better than the first version!</p><p>He can write some functions now. First up is <code>update_stock()</code>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rNJj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rNJj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png 424w, https://substackcdn.com/image/fetch/$s_!rNJj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png 848w, https://substackcdn.com/image/fetch/$s_!rNJj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png 1272w, https://substackcdn.com/image/fetch/$s_!rNJj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!rNJj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png" width="1200" height="462" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:462,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:66559,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rNJj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png 424w, https://substackcdn.com/image/fetch/$s_!rNJj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png 848w, https://substackcdn.com/image/fetch/$s_!rNJj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png 1272w, https://substackcdn.com/image/fetch/$s_!rNJj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#3</figcaption></figure></div><p>Let&#8217;s ignore the fact that Mark is accessing the global variable <code>products</code> within the function. We&#8217;ll have a chat with Mark about this.</p><p>Still, there&#8217;s now a function called <code>update_stock()</code>. It needs the product name as an argument. It also needs the stock amount to increase (or decrease if the value is negative).</p><p>Let&#8217;s look at another function Mark wrote:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hZ4n!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hZ4n!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png 424w, https://substackcdn.com/image/fetch/$s_!hZ4n!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png 848w, https://substackcdn.com/image/fetch/$s_!hZ4n!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png 1272w, https://substackcdn.com/image/fetch/$s_!hZ4n!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!hZ4n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png" width="1200" height="840" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:840,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:123883,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hZ4n!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png 424w, https://substackcdn.com/image/fetch/$s_!hZ4n!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png 848w, https://substackcdn.com/image/fetch/$s_!hZ4n!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png 1272w, https://substackcdn.com/image/fetch/$s_!hZ4n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#4</figcaption></figure></div><p>This function also needs the product name as one of its arguments. It also needs a second argument: the quantity sold.</p><p>Mark wrote other functions, but I&#8217;ll keep this section brief so I won&#8217;t show them. However, many of Mark&#8217;s functions have a few things in common:</p><ul><li><p>They require the product name as one of the arguments. This makes sense since the operations Mark needs to carry out depend on which product he&#8217;s dealing with.</p></li><li><p>They make changes to the data in one of the inner dictionaries defined within <code>products</code>.</p></li><li><p>Some of them return data. Others don&#8217;t.</p></li></ul><p>Great. Mark is happy with his effort.</p><p>He structured the data sensibly so it&#8217;s well organised. Separately, he wrote functions that use data from those data structures&#8211;the inner dictionaries in <code>products</code> and sometimes make changes to the data in those data structures.</p><div><hr></div><p><em>Your call&#8230;</em></p><p><em>The Python Coding Place offers something for everyone:</em></p><p><em>&#8226; a super-personalised one-to-one 6-month mentoring option</em><br><em>$ 4,750</em></p><p><em>&#8226; individual one-to-one sessions</em><br><em>$ 125</em></p><p><em>&#8226; a self-led route with access to 60+ hrs of exceptional video courses and a support forum</em><br><em>$ 400</em></p><p><em><strong><a href="https://thepythoncodingplace.com?utm_source=the-python-coding-stack">Which The Python Coding Place student are you?</a></strong></em></p><div><hr></div><h2><strong>Third Version &#8226; Class and Objects</strong></h2><p>Mark&#8217;s code stores data. The nested dictionaries in <code>products</code> deal with the storage part. His code also does stuff* with the data through the functions he wrote.</p><p><em>*stuff is not quite a Python technical term, in case you&#8217;re wondering. But it&#8217;s quite suitable here, I think!</em></p><p>Lots of programs store data and do stuff with data.</p><p>Object-oriented programming takes these two separate tasks&#8211;storing data and doing stuff with data&#8211;and combines them into a single &#8220;unit&#8221;. In OOP, this &#8220;unit&#8221; is the <em>object</em>. Objects are at the centre of the OOP paradigm, which is why OOP is called OOP, after all!</p><ol><li><p>Mark&#8217;s progression from the first version to the second relied on structuring the data into units&#8211;the nested dictionary structure.</p></li><li><p>The progression from the second version to the third takes this a step further and structures the data <em>and</em> the functions that act on those data into a single unit: the object.</p></li></ol><p>This structure means that the data for each object is contained within the object, and the actions performed are also contained within that object. Everything is self-contained within the object.</p><p>Let&#8217;s build a <code>Product</code> class for Mark. But let&#8217;s do this step by step, using the code from the second version and gradually morphing it into object-oriented code. This will help us follow the transition.</p><p>First, let&#8217;s create the class:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!z1i5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5143cf31-32cd-4d87-b117-a0ff79ed70f6_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!z1i5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5143cf31-32cd-4d87-b117-a0ff79ed70f6_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!z1i5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5143cf31-32cd-4d87-b117-a0ff79ed70f6_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!z1i5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5143cf31-32cd-4d87-b117-a0ff79ed70f6_1200x210.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!z1i5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5143cf31-32cd-4d87-b117-a0ff79ed70f6_1200x210.png" width="1200" height="210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5143cf31-32cd-4d87-b117-a0ff79ed70f6_1200x210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:210,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7003,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5143cf31-32cd-4d87-b117-a0ff79ed70f6_1200x210.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!z1i5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5143cf31-32cd-4d87-b117-a0ff79ed70f6_1200x210.png 424w, https://substackcdn.com/image/fetch/$s_!z1i5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5143cf31-32cd-4d87-b117-a0ff79ed70f6_1200x210.png 848w, https://substackcdn.com/image/fetch/$s_!z1i5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5143cf31-32cd-4d87-b117-a0ff79ed70f6_1200x210.png 1272w, https://substackcdn.com/image/fetch/$s_!z1i5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5143cf31-32cd-4d87-b117-a0ff79ed70f6_1200x210.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#5</figcaption></figure></div><p>Nothing much to see so far. But this is the shell that will contain all the instructions for creating this &#8220;unit,&#8221; including all the relevant data and functionality.</p><p>You can already create an <em>instance</em> of this class &#8211; that&#8217;s another way of saying <em>an object created from this class</em>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-uje!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-uje!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png 424w, https://substackcdn.com/image/fetch/$s_!-uje!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png 848w, https://substackcdn.com/image/fetch/$s_!-uje!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png 1272w, https://substackcdn.com/image/fetch/$s_!-uje!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!-uje!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png" width="1200" height="336" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:336,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12896,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-uje!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png 424w, https://substackcdn.com/image/fetch/$s_!-uje!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png 848w, https://substackcdn.com/image/fetch/$s_!-uje!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png 1272w, https://substackcdn.com/image/fetch/$s_!-uje!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#6</figcaption></figure></div><p>Whereas <code>Product</code> represents the class, <code>Product()</code> is an instance of the class. Note the parentheses. There&#8217;s only one <code>Product</code>, but you can create many instances using <code>Product()</code> .</p><h3><strong>Bundling In the Functionality &#8226; Methods</strong></h3><p>Now, let me take a route I recently explored for the first time while teaching OOP in a beginners&#8217; course. It&#8217;s only subtly different from my &#8220;usual&#8221; teaching route, but I think it helped me appreciate the topic from a distinct perspective. So, here we go.</p><p>Let&#8217;s copy and paste the functions from version two directly into this class. Warning: This won&#8217;t work. We&#8217;ll need to make some changes. But let&#8217;s use this as a starting point:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-Iap!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-Iap!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png 424w, https://substackcdn.com/image/fetch/$s_!-Iap!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png 848w, https://substackcdn.com/image/fetch/$s_!-Iap!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png 1272w, https://substackcdn.com/image/fetch/$s_!-Iap!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!-Iap!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png" width="1272" height="1260" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1260,&quot;width&quot;:1272,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:208295,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-Iap!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png 424w, https://substackcdn.com/image/fetch/$s_!-Iap!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png 848w, https://substackcdn.com/image/fetch/$s_!-Iap!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png 1272w, https://substackcdn.com/image/fetch/$s_!-Iap!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#7</figcaption></figure></div><p>To keep you on your toes, we change the terminology here. These are functions. However, when they&#8217;re defined within a class definition, we call them <em>methods</em>. But they&#8217;re still functions. Everything you know about functions applies equally to methods.</p><p>You&#8217;ve seen that each function in version two needed to know which product it was dealing with. That&#8217;s why the first parameter is <code>product_name</code>. You used <code>product_name</code> to fetch the correct values when you needed the selling price, the cost price, or the number of items in stock.</p><p>However, now that we&#8217;re in the OOP domain, the object will contain all the data it needs&#8211;you&#8217;ll add the data-related code soon. You don&#8217;t need to fetch the data from anywhere else, just from the object <em>itself</em>.</p><p>Therefore, the functions defined within the class &#8211; the methods &#8211; no longer need the product name as the first argument. Instead, they need the entire object <em>itself</em> since this object contains all the data the method needs about the object.</p><p>We just need a parameter name to describe the object <em>itself</em>. How about <code>self</code>?</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!F7-U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!F7-U!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png 424w, https://substackcdn.com/image/fetch/$s_!F7-U!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png 848w, https://substackcdn.com/image/fetch/$s_!F7-U!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png 1272w, https://substackcdn.com/image/fetch/$s_!F7-U!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!F7-U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png" width="1200" height="378" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:378,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:42027,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!F7-U!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png 424w, https://substackcdn.com/image/fetch/$s_!F7-U!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png 848w, https://substackcdn.com/image/fetch/$s_!F7-U!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png 1272w, https://substackcdn.com/image/fetch/$s_!F7-U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#8</figcaption></figure></div><p>The method signatures now include <code>self</code> as the first parameter and whatever else the methods need as the remaining parameters.</p><p>Incidentally, although you can technically name this first parameter anything you want, there&#8217;s a strong convention to always use <code>self</code>. So it&#8217;s best to stick with <code>self</code>!</p><p>The methods are the tools you use to bundle functionality into the object. Let&#8217;s pause on working on the methods for now and shift our attention to how to store the data, which also needs to be bundled into the object.</p><h3><strong>Bundling In the Data &#8226; Data Attributes</strong></h3><p>Let&#8217;s get back to the code that creates an instance of the <code>Product</code> class:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ELDx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb77c2438-d948-4408-abd0-cd2eef82a455_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!ELDx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb77c2438-d948-4408-abd0-cd2eef82a455_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!ELDx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb77c2438-d948-4408-abd0-cd2eef82a455_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!ELDx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb77c2438-d948-4408-abd0-cd2eef82a455_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ELDx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb77c2438-d948-4408-abd0-cd2eef82a455_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b77c2438-d948-4408-abd0-cd2eef82a455_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8291,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb77c2438-d948-4408-abd0-cd2eef82a455_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ELDx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb77c2438-d948-4408-abd0-cd2eef82a455_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!ELDx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb77c2438-d948-4408-abd0-cd2eef82a455_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!ELDx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb77c2438-d948-4408-abd0-cd2eef82a455_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!ELDx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb77c2438-d948-4408-abd0-cd2eef82a455_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#9</figcaption></figure></div><p>The expression <code>Product()</code> does a few things behind the scenes. First, it creates a new blank object. Then it initialises this object. To initialise the object, Python needs to &#8220;do stuff&#8221;. Therefore, it needs a method. But not just any method. A special method:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Tw81!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Tw81!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png 424w, https://substackcdn.com/image/fetch/$s_!Tw81!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png 848w, https://substackcdn.com/image/fetch/$s_!Tw81!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png 1272w, https://substackcdn.com/image/fetch/$s_!Tw81!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Tw81!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png" width="1200" height="420" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:420,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:39562,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Tw81!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png 424w, https://substackcdn.com/image/fetch/$s_!Tw81!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png 848w, https://substackcdn.com/image/fetch/$s_!Tw81!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png 1272w, https://substackcdn.com/image/fetch/$s_!Tw81!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#10</figcaption></figure></div><p>You can&#8217;t choose the name of this method. It must be <code>.__init__()</code>. But it&#8217;s still a method. It still takes <code>self</code> as the first parameter since it still needs access to the object itself. This method creates variables that are attached to the object. You can think of the dot as attaching <code>name</code> to <code>self</code> and so on for the others. We tend not to call these variables &#8211; more new terminology just for the OOP paradigm &#8211; they&#8217;re <em>data attributes</em>. They&#8217;re object attributes that store data.</p><p>For now, these data attributes contain default values: the empty string for the <code>.name</code> data attribute and <code>0</code> for the others. However, when you create an object, you often want to supply some or all of the data that the object needs. Often, you want to pass this information when you create the object:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1OUE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fb4f53a-64a7-4719-a7e6-9b4e658372a6_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!1OUE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fb4f53a-64a7-4719-a7e6-9b4e658372a6_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!1OUE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fb4f53a-64a7-4719-a7e6-9b4e658372a6_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!1OUE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fb4f53a-64a7-4719-a7e6-9b4e658372a6_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!1OUE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fb4f53a-64a7-4719-a7e6-9b4e658372a6_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8fb4f53a-64a7-4719-a7e6-9b4e658372a6_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:14142,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fb4f53a-64a7-4719-a7e6-9b4e658372a6_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1OUE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fb4f53a-64a7-4719-a7e6-9b4e658372a6_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!1OUE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fb4f53a-64a7-4719-a7e6-9b4e658372a6_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!1OUE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fb4f53a-64a7-4719-a7e6-9b4e658372a6_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!1OUE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fb4f53a-64a7-4719-a7e6-9b4e658372a6_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#11</figcaption></figure></div><p>However, when you run the code, you get an error:</p><pre><code>Traceback (most recent call last):
  ...
    a_product = Product(&#8221;Coffee&#8221;, 1.98, 3.2, 100)
TypeError: Product.__init__() takes 1 positional 
    argument but 5 were given</code></pre><p>The error message mentions <code>Product.__init__()</code>. Note how you don&#8217;t explicitly use <code>.__init__()</code> in the expression to create a <code>Product</code> object. However, Python calls this special method behind the scenes. And when it does, it complains about the arguments you passed:</p><ul><li><p><code>"Product.__init__() takes 1 positional argument..."</code> is the first part of the error message. Makes sense, since you have <code>self</code> as the one and only parameter within the definition of the <code>.__init__()</code> special method.</p></li><li><p><code>"...but 5 [arguments] were given"</code>, the error message goes on to say. Wait, why 5? You pass four objects when you create <code>Product()</code>: the string <code>"Coffee"</code>, the floats <code>1.98</code> and <code>3.2</code>, and the integer <code>100</code>. Python can&#8217;t count, it seems?</p></li></ul><p>Not quite. When Python calls a method, it automatically passes the object as the first argument. You don&#8217;t see this. You don&#8217;t need to do anything about it. It happens behind the scenes. So, soon after <code>Product()</code> creates a blank new instance, it calls <code>.__init__()</code> and passes the object as the first argument to <code>.__init__()</code>, the one that&#8217;s assigned to the parameter <code>self</code>.</p><p>Recall that methods are functions. So, Python automatically passes the object as the first argument so that these functions (methods) have access to the object.</p><p>That&#8217;s why the error message says that five arguments were passed: the object itself and the four remaining arguments.</p><p>This tells you that <code>.__init__()</code> needs five parameters. The first is <code>self</code>, which is the first parameter in these methods. Let&#8217;s add the remaining four:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KdEU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F606da463-e4b2-4120-b097-0974a5d66597_1218x378.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KdEU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F606da463-e4b2-4120-b097-0974a5d66597_1218x378.png 424w, https://substackcdn.com/image/fetch/$s_!KdEU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F606da463-e4b2-4120-b097-0974a5d66597_1218x378.png 848w, https://substackcdn.com/image/fetch/$s_!KdEU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F606da463-e4b2-4120-b097-0974a5d66597_1218x378.png 1272w, https://substackcdn.com/image/fetch/$s_!KdEU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F606da463-e4b2-4120-b097-0974a5d66597_1218x378.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!KdEU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F606da463-e4b2-4120-b097-0974a5d66597_1218x378.png" width="1218" height="378" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/606da463-e4b2-4120-b097-0974a5d66597_1218x378.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:378,&quot;width&quot;:1218,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:37034,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F606da463-e4b2-4120-b097-0974a5d66597_1218x378.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!KdEU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F606da463-e4b2-4120-b097-0974a5d66597_1218x378.png 424w, https://substackcdn.com/image/fetch/$s_!KdEU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F606da463-e4b2-4120-b097-0974a5d66597_1218x378.png 848w, https://substackcdn.com/image/fetch/$s_!KdEU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F606da463-e4b2-4120-b097-0974a5d66597_1218x378.png 1272w, https://substackcdn.com/image/fetch/$s_!KdEU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F606da463-e4b2-4120-b097-0974a5d66597_1218x378.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#12</figcaption></figure></div><p>The code no longer raises an error since the number of arguments passed to <code>Product()</code> when you create the object &#8211; including the object itself, which is implied &#8211; matches the number of parameters in <code>.__init__()</code>. However, you want to shift the data into the data attributes you created earlier. These data attributes are the storage devices attached to the object. You want the data to be stored there:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ee1j!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ee1j!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png 424w, https://substackcdn.com/image/fetch/$s_!Ee1j!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png 848w, https://substackcdn.com/image/fetch/$s_!Ee1j!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png 1272w, https://substackcdn.com/image/fetch/$s_!Ee1j!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ee1j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png" width="1218" height="378" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:378,&quot;width&quot;:1218,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:35205,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ee1j!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png 424w, https://substackcdn.com/image/fetch/$s_!Ee1j!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png 848w, https://substackcdn.com/image/fetch/$s_!Ee1j!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png 1272w, https://substackcdn.com/image/fetch/$s_!Ee1j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#13</figcaption></figure></div><p>The parameters are used only to transfer the data from the method call to the data attributes. From now on, the data are stored in the data attributes, which are attached to the object.</p><p>Note that the data attribute names don&#8217;t have to match the parameter names. But why bother coming up with different names? Might as well use the same ones!</p><p>Now, you can create any object you wish, each having its own data:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!02LF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!02LF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png 424w, https://substackcdn.com/image/fetch/$s_!02LF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png 848w, https://substackcdn.com/image/fetch/$s_!02LF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png 1272w, https://substackcdn.com/image/fetch/$s_!02LF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!02LF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png" width="1200" height="546" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:546,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55983,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!02LF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png 424w, https://substackcdn.com/image/fetch/$s_!02LF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png 848w, https://substackcdn.com/image/fetch/$s_!02LF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png 1272w, https://substackcdn.com/image/fetch/$s_!02LF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#14</figcaption></figure></div><p>Here&#8217;s the output:</p><pre><code>Coffee
Tea
3.2
2.25</code></pre><p>Every instance of the <code>Product</code> class will have a <code>.name</code>, <code>.cost_price</code>, <code>.selling_price</code>, and <code>.stock</code>. But each instance will have its own values for those data attributes. Each object is distinct from the others. The data is self-contained within the object.</p><h3><strong>Back to the Functionality &#8226; Methods</strong></h3><p>Let&#8217;s look at the class so far, which still has code pasted from version two earlier:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7mVZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7mVZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png 424w, https://substackcdn.com/image/fetch/$s_!7mVZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png 848w, https://substackcdn.com/image/fetch/$s_!7mVZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png 1272w, https://substackcdn.com/image/fetch/$s_!7mVZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!7mVZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png" width="1272" height="1512" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1512,&quot;width&quot;:1272,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:234542,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7mVZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png 424w, https://substackcdn.com/image/fetch/$s_!7mVZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png 848w, https://substackcdn.com/image/fetch/$s_!7mVZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png 1272w, https://substackcdn.com/image/fetch/$s_!7mVZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#15</figcaption></figure></div><p>Let&#8217;s focus on <code>.update_stock()</code> first. Note how in my writing style guide, I use a leading dot when writing method names, such as <code>.update_stock()</code>. That&#8217;s because they&#8217;re also attributes of the object. To call a method, you call it through the object, such as <code>a_product.update_stock(30)</code>.</p><p>Recall that Python will pass the object <em>itself</em> as the first argument to the method. That&#8217;s the argument assigned to <code>self</code>. You then pass <code>30</code> to the parameter <code>stock_increment</code>.</p><p>But this means that this method can only act on an object that already exists, that already has the data attributes it needs. You no longer need to check whether the product exists. If you&#8217;re calling this method, you&#8217;re calling it on a product. Checks on existence will happen elsewhere in your code.</p><p>So, all that&#8217;s left is to increment the stock for this object. The current stock is stored in the <code>.stock</code> data attribute &#8211; the data attribute that&#8217;s connected to the object. But you passed the object to <code>.update_stock()</code>, so you can access <code>self.stock</code> from within the method:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!N97H!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2683369b-d547-42c0-b821-dfd60661d0e9_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!N97H!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2683369b-d547-42c0-b821-dfd60661d0e9_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!N97H!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2683369b-d547-42c0-b821-dfd60661d0e9_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!N97H!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2683369b-d547-42c0-b821-dfd60661d0e9_1200x252.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!N97H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2683369b-d547-42c0-b821-dfd60661d0e9_1200x252.png" width="1200" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2683369b-d547-42c0-b821-dfd60661d0e9_1200x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19884,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2683369b-d547-42c0-b821-dfd60661d0e9_1200x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!N97H!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2683369b-d547-42c0-b821-dfd60661d0e9_1200x252.png 424w, https://substackcdn.com/image/fetch/$s_!N97H!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2683369b-d547-42c0-b821-dfd60661d0e9_1200x252.png 848w, https://substackcdn.com/image/fetch/$s_!N97H!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2683369b-d547-42c0-b821-dfd60661d0e9_1200x252.png 1272w, https://substackcdn.com/image/fetch/$s_!N97H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2683369b-d547-42c0-b821-dfd60661d0e9_1200x252.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">#16</figcaption></figure></div><p>And that&#8217;s it. Everything is self-contained within the object. The method acts on the object and modifies one of the object&#8217;s data attributes.</p><p>How about the <code>.sell()</code> method?</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qOzq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qOzq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png 424w, https://substackcdn.com/image/fetch/$s_!qOzq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png 848w, https://substackcdn.com/image/fetch/$s_!qOzq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png 1272w, https://substackcdn.com/image/fetch/$s_!qOzq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!qOzq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png" width="1272" height="630" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:630,&quot;width&quot;:1272,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:78361,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qOzq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png 424w, https://substackcdn.com/image/fetch/$s_!qOzq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png 848w, https://substackcdn.com/image/fetch/$s_!qOzq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png 1272w, https://substackcdn.com/image/fetch/$s_!qOzq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#17</figcaption></figure></div><p>Again, you can remove the validation to check that the product exists. A method is called on a <code>Product</code> object, so it exists. This method then uses three of the object&#8217;s data attributes to perform its task.</p><p>Note that you can add similar validation in <code>.update_stock()</code> to ensure the stock doesn&#8217;t dip below <code>0</code>. However, <code>stock_increment</code> can be negative to enable reducing the stock. You could even use <code>.update_stock()</code> within <code>.sell()</code> if you wish.</p><div><hr></div><p>Ah, what about the biscuits? You can still use a list of products, but this time the list contains <code>Product</code> objects:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YJBc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YJBc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png 424w, https://substackcdn.com/image/fetch/$s_!YJBc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png 848w, https://substackcdn.com/image/fetch/$s_!YJBc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png 1272w, https://substackcdn.com/image/fetch/$s_!YJBc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!YJBc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png" width="1200" height="378" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:378,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:33140,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!YJBc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png 424w, https://substackcdn.com/image/fetch/$s_!YJBc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png 848w, https://substackcdn.com/image/fetch/$s_!YJBc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png 1272w, https://substackcdn.com/image/fetch/$s_!YJBc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#18</figcaption></figure></div><p>Or, if you prefer, you can use a dictionary:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jUm2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jUm2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png 424w, https://substackcdn.com/image/fetch/$s_!jUm2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png 848w, https://substackcdn.com/image/fetch/$s_!jUm2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png 1272w, https://substackcdn.com/image/fetch/$s_!jUm2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!jUm2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png" width="1200" height="336" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:336,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:33414,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jUm2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png 424w, https://substackcdn.com/image/fetch/$s_!jUm2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png 848w, https://substackcdn.com/image/fetch/$s_!jUm2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png 1272w, https://substackcdn.com/image/fetch/$s_!jUm2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#19</figcaption></figure></div><p>Let&#8217;s trial out this dictionary and the <code>Product</code> class:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BaYC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BaYC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png 424w, https://substackcdn.com/image/fetch/$s_!BaYC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png 848w, https://substackcdn.com/image/fetch/$s_!BaYC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png 1272w, https://substackcdn.com/image/fetch/$s_!BaYC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!BaYC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png" width="1200" height="756" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:756,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:89449,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!BaYC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png 424w, https://substackcdn.com/image/fetch/$s_!BaYC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png 848w, https://substackcdn.com/image/fetch/$s_!BaYC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png 1272w, https://substackcdn.com/image/fetch/$s_!BaYC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">#20</figcaption></figure></div><p>In this basic use case, you use the item input by the user to fetch the corresponding <code>Product</code> object from the <code>products</code> dictionary. You assign this object to the variable name <code>product</code>. Then you can call its methods, such as <code>.sell()</code>, and access its data attributes, such as <code>.name</code> and <code>.stock</code>.</p><p>Here&#8217;s the output from this code, including sample user inputs:</p><pre><code>Enter item: coffee
Enter quantity of Coffee sold: 3
The income from this sale is &#163;9.60
The profit from this sale is &#163;3.66
Remaining Coffee units: 97</code></pre><h2><strong>Final Words</strong></h2><p>The aim of this article is not to provide a comprehensive and exhaustive walkthrough of OOP. I wrote elsewhere about OOP. You can start from <em><a href="https://thepythoncodingplace.com/the-python-coding-book/">The Python Coding Book</a></em> and then read the seven-part series <em><a href="https://www.thepythoncodingstack.com/p/a-magical-tour-through-object-oriented">A Magical Tour Through Object-Oriented Programming in Python &#8226; Hogwarts School of Codecraft and Algorithmancy</a></em>.</p><p>You can learn about the syntax and the mechanics of defining and using classes. And that&#8217;s important if you want to write classes. But just as importantly, you need to adopt an OOP mindset. A central point is how OOP bundles data and functionality into a single unit, the object, and how everything stems from that structure.</p><p>In this post, you saw how data is bundled into the object through data attributes. And you bundled the high-level functionality that matters for your object through the methods <code>.update_stock()</code> and <code>.sell()</code>. Mark will need more of these methods, as you can imagine.</p><p>However, the object also includes low-level functionality. What should Python do when it needs to print the object? How about if it needs to add it to another object? Should that be possible? Should the object be considered <code>False</code> under any circumstance, say? These operations are defined by an object&#8217;s special methods, also known as dunder methods. I proposed thinking of these as &#8220;plumbing methods&#8221; recently: <a href="https://www.thepythoncodingstack.com/p/pythons-plumbing-is-not-as-flashy">&#8220;Python&#8217;s Plumbing&#8221; Is Not As Flashy as &#8220;Magic Methods&#8221; &#8226; But Is It Better?</a></p><p>Therefore, there&#8217;s plenty of high-level and low-level functionality bundled within the object. And the data, of course.</p><p>Here&#8217;s another relatively recent post you may enjoy in case you missed it when it was published: <a href="https://www.thepythoncodingstack.com/p/my-life-the-autobiography-of-a-python-object">My Life &#8226; The Autobiography of a Python Object</a>.</p><p>In summary, OOP structures data and functionality into a single unit &#8211; the object. Then, your code is oriented around this object.</p><div class="pullquote"><p><em>Do you want to master Python one article at a time? Then don&#8217;t miss out on the articles in The Club which are exclusive to premium subscribers here on The Python Coding Stack</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!X8ae!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!X8ae!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg 424w, https://substackcdn.com/image/fetch/$s_!X8ae!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg 848w, https://substackcdn.com/image/fetch/$s_!X8ae!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!X8ae!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!X8ae!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg" width="622" height="829.190934065934" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1941,&quot;width&quot;:1456,&quot;resizeWidth&quot;:622,&quot;bytes&quot;:1368065,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/190048208?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!X8ae!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg 424w, https://substackcdn.com/image/fetch/$s_!X8ae!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg 848w, https://substackcdn.com/image/fetch/$s_!X8ae!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!X8ae!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Photo by <a href="https://www.pexels.com/photo/bamboo-sticks-tied-together-in-bundles-5607157/">gomed fashion</a></em></p><div><hr></div><p><em>Code in this article uses Python 3.14</em></p><p><em>The code images used in this article are created using <a href="https://snappify.cello.so/f4AsFrwgwov">Snappify</a>.</em> <em>[Affiliate link]</em></p><p><em><strong><a href="https://www.thepythoncodingstack.com/subscribe">Join</a></strong></em><strong><a href="https://www.thepythoncodingstack.com/subscribe"> The Club</a></strong><em>, the exclusive area for paid subscribers for <a href="https://www.thepythoncodingstack.com/s/the-club">more Python posts</a>, videos, a members&#8217; forum, and more.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.thepythoncodingstack.com/subscribe?"><span>Subscribe now</span></a></p><p><em>You can also support this publication by making a <a href="https://buy.stripe.com/00g3de2iGdgg4gg7su">one-off contribution of any amount you wish</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://buy.stripe.com/00g3de2iGdgg4gg7su&quot;,&quot;text&quot;:&quot;Support The Python Coding Stack&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://buy.stripe.com/00g3de2iGdgg4gg7su"><span>Support The Python Coding Stack</span></a></p><div><hr></div><p><em>For more Python resources, you can also visit</em> <em><a href="https://realpython.com?utm_source=the-python-coding-stack">Real Python</a>&#8212;you may even stumble on one of my own articles or courses there!</em></p><p><em>Also, are you interested in technical writing? You&#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at</em> <em><a href="http://stephengruppetta.com/breaking-the-rules">Breaking the Rules</a></em>.</p><p><em>And you can find out more about me at</em> <em><a href="https://stephengruppetta.com?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p><p>Further reading related to this article&#8217;s topic:</p><ul><li><p><a href="https://thepythoncodingplace.com/the-python-coding-book/">The Python Coding Book</a></p></li><li><p><a href="https://www.thepythoncodingstack.com/p/a-magical-tour-through-object-oriented">A Magical Tour Through Object-Oriented Programming in Python &#8226; Hogwarts School of Codecraft and Algorithmancy</a></p></li><li><p><a href="https://www.thepythoncodingstack.com/p/my-life-the-autobiography-of-a-python-object">My Life &#8226; The Autobiography of a Python Object</a></p></li></ul><div><hr></div><h2>Appendix: Code Blocks</h2><h5>Code Block #1</h5><pre><code><code>products = ["Coffee", "Tea", "Biscuits"]
cost_price = [1.98, 0.85, 3.1]
selling_price = [3.2, 2.25, 5]
stock = [100, 200, 25]
</code></code></pre><h5>Code Block #2</h5><pre><code><code>products = {
    "Coffee": {
        "cost_price": 1.98,
        "selling_price": 3.2,
        "stock": 100,
    },
    "Tea": {
        "cost_price": 0.85,
        "selling_price": 2.25,
        "stock": 200,
    },
    "Biscuits": {
        "cost_price": 3.1,
        "selling_price": 5,
        "stock": 25,
    },
}
</code></code></pre><h5>Code Block #3</h5><pre><code><code># ...
def update_stock(product_name, stock_increment):
    if product_name not in products:
        raise KeyError(f"Product {product_name} not found")
    products[product_name]["stock"] += stock_increment
    # We'll need to have a chat with Mark about the issues
    # with accessing global variables from functions.
    # Let's let it pass here!
</code></code></pre><h5>Code Block #4</h5><pre><code><code># ...
def sell(product_name, quantity):
    if product_name not in products:
        raise KeyError(f"Product {product_name} not found")
    if quantity &lt;= 0:
        raise ValueError("Quantity must be greater than zero")
    if products[product_name]["stock"] &lt; quantity:
        raise ValueError("Not enough stock")
        
    products[product_name]["stock"] -= quantity
    selling_price = products[product_name]["selling_price"]
    cost_price = products[product_name]["cost_price"]

    income = selling_price * quantity
    profit = (selling_price - cost_price) * quantity

    return income, profit
</code></code></pre><h5>Code Block #5</h5><pre><code><code>class Product:
    ...
</code></code></pre><h5>Code Block #6</h5><pre><code><code>class Product:
    ...
    

a_product = Product()
</code></code></pre><h5>Code Block #7</h5><pre><code><code>class Product:
    # Just pasting the functions from Version 2 here for now.
    # This still doesn't work. It's a work-in-progress
    def update_stock(product_name, stock_increment):
        if product_name not in products:
            raise KeyError(f"Product {product_name} not found")
        products[product_name]["stock"] += stock_increment
        # We'll need to have a chat with Mark about the issues
        # with accessing global variables from functions.
        # Let's let it pass here!

    def sell(product_name, quantity):
        if product_name not in products:
            raise KeyError(f"Product {product_name} not found")
        if quantity &lt;= 0:
            raise ValueError("Quantity must be greater than zero")
        if products[product_name]["stock"] &lt; quantity:
            raise ValueError("Not enough stock")

        products[product_name]["stock"] -= quantity
        selling_price = products[product_name]["selling_price"]
        cost_price = products[product_name]["cost_price"]

        income = selling_price * quantity
        profit = (selling_price - cost_price) * quantity

        return income, profit
</code></code></pre><h5>Code Block #8</h5><pre><code><code>class Product:
    def update_stock(self, stock_increment):
        # rest of code pasted from version two (for now)

    def sell(self, quantity):
        # rest of code pasted from version two (for now)
</code></code></pre><h5>Code Block #9</h5><pre><code><code># ...

a_product = Product()
</code></code></pre><h5>Code Block #10</h5><pre><code><code>class Product:
    # We'll make improvements to this method soon
    def __init__(self):
        self.name = ""
        self.cost_price = 0
        self.selling_price = 0
        self.stock = 0
</code></code></pre><h5>Code Block #11</h5><pre><code><code># ...

a_product = Product("Coffee", 1.98, 3.2, 100)
</code></code></pre><h5>Code Block #12</h5><pre><code><code>class Product:
    def __init__(self, name, cost_price, selling_price, stock):
        self.name = ""
        self.cost_price = 0
        self.selling_price = 0
        self.stock = 0
</code></code></pre><h5>Code Block #13</h5><pre><code><code>class Product:
    def __init__(self, name, cost_price, selling_price, stock):
        self.name = name
        self.cost_price = cost_price
        self.selling_price = selling_price
        self.stock = stock
</code></code></pre><h5>Code Block #14</h5><pre><code><code># ...

a_product = Product("Coffee", 1.98, 3.2, 100)
another_product = Product("Tea", 0.85, 2.25, 200)

print(a_product.name)
print(another_product.name)

print(a_product.selling_price)
print(another_product.selling_price)
</code></code></pre><h5>Code Block #15</h5><pre><code><code>class Product:
    def __init__(self, name, cost_price, selling_price, stock):
        self.name = name
        self.cost_price = cost_price
        self.selling_price = selling_price
        self.stock = stock

    # Just pasting the functions from Version 2 here for now.
    # This still doesn't work. It's a work-in-progress
    def update_stock(self, stock_increment):
        if product_name not in products:
            raise KeyError(f"Product {product_name} not found")
        products[product_name]["stock"] += stock_increment
        # We'll need to have a chat with Mark about the issues
        # with accessing global variables from functions.
        # Let's let it pass here!

    def sell(self, quantity):
        if product_name not in products:
            raise KeyError(f"Product {product_name} not found")
        if quantity &lt;= 0:
            raise ValueError("Quantity must be greater than zero")
        if products[product_name]["stock"] &lt; quantity:
            raise ValueError("Not enough stock")

        products[product_name]["stock"] -= quantity
        selling_price = products[product_name]["selling_price"]
        cost_price = products[product_name]["cost_price"]

        income = selling_price * quantity
        profit = (selling_price - cost_price) * quantity

        return income, profit
</code></code></pre><h5>Code Block #16</h5><pre><code><code># ...
    def update_stock(self, stock_increment):
        self.stock += stock_increment
</code></code></pre><h5>Code Block #17</h5><pre><code><code># ...
    def sell(self, quantity):
        if quantity &lt;= 0:
            raise ValueError("Quantity must be greater than zero")
        if self.stock &lt; quantity:
            raise ValueError("Not enough stock")

        self.stock -= quantity
        income = self.selling_price * quantity
        profit = (self.selling_price - self.cost_price) * quantity

        return income, profit
</code></code></pre><h5>Code Block #18</h5><pre><code><code># ...
products = [
    Product("Coffee", 1.98, 3.2, 100),
    Product("Tea", 0.85, 2.25, 200),
    Product("Biscuits", 3.1, 5, 25),
]
</code></code></pre><h5>Code Block #19</h5><pre><code><code>products = {
    "Coffee": Product("Coffee", 1.98, 3.2, 100),
    "Tea": Product("Tea", 0.85, 2.25, 200),
    "Biscuits": Product("Biscuits", 3.1, 5, 25),
}
</code></code></pre><h5>Code Block #20</h5><pre><code><code># ...

# I've kept this demo code brief and simple, so it lacks
# verification and robustness. You can add this if you wish!

item = input("Enter item: ").title()
qty = int(input(f"Enter quantity of {item} sold: "))

product = products[item]
income, profit = product.sell(qty)

print(f"The income from this sale is &#163;{income:.2f}")
print(f"The profit from this sale is &#163;{profit:.2f}")

print(f"Remaining {product.name} units: {product.stock}")
</code></code></pre><div><hr></div><p><em>For more Python resources, you can also visit</em> <em><a href="https://realpython.com?utm_source=the-python-coding-stack">Real Python</a>&#8212;you may even stumble on one of my own articles or courses there!</em></p><p><em>Also, are you interested in technical writing? You&#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at</em> <em><a href="http://stephengruppetta.com/breaking-the-rules">Breaking the Rules</a></em>.</p><p><em>And you can find out more about me at</em> <em><a href="https://stephengruppetta.com?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p>]]></content:encoded></item><item><title><![CDATA[“Python’s Plumbing” Is Not As Flashy as “Magic Methods” • But Is It Better?]]></title><description><![CDATA[Dunder methods are the pipes and valves that are out of sight, but that keep everything flowing in Python]]></description><link>https://www.thepythoncodingstack.com/p/pythons-plumbing-is-not-as-flashy</link><guid isPermaLink="false">https://www.thepythoncodingstack.com/p/pythons-plumbing-is-not-as-flashy</guid><dc:creator><![CDATA[Stephen Gruppetta]]></dc:creator><pubDate>Tue, 24 Feb 2026 13:56:05 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/87ba71f7-042a-4178-adc7-e69de2f2f01e_1920x1380.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You&#8217;ve heard this phrase before: &#8220;Everything is an object in Python&#8221;. But here&#8217;s another phrase that&#8217;s related but rather less catchy:</p><blockquote><p>Everything goes through special methods (dunder methods) in Python</p></blockquote><p>Special methods are everywhere. However, you don&#8217;t see them. In fact, you&#8217;re not meant to use them directly unless you&#8217;re defining them within a class. They&#8217;re out of sight. But they keep everything moving smoothly in Python.</p><p>Here&#8217;s a short essay exploring where these special methods fit within Python.</p><h2><strong>Why Should I Care About Special Methods?</strong></h2><p>&#8220;Everything goes through special methods in Python.&#8221; I should probably preface the phrase with &#8220;almost&#8221;, but the phrase is already unwieldy as it is.</p><p>You want to display an object on the screen? Python looks for guidance in <code>.__str__()</code> or <code>.__repr__()</code>.</p><p>You want to use the object in a <code>for</code> loop? Is it even possible? Python looks for <code>.__iter__()</code> to check whether it can iterate over the object and how.</p><p>Do you want to fetch an individual item from the object? If this makes sense for this object, then it will have a <code>.__getitem__()</code> special method.</p><p>How should Python interpret an object&#8217;s truthiness? There&#8217;s <code>.__bool__()</code> for that. Or <code>.__len__()</code>!</p><p>Ah, you&#8217;d like to add an object to another object. Does your object have a <code>.__add__()</code> special method?</p><p>I could go on. I&#8217;ll post links to articles that cover some of these special methods in more detail below.</p><p>I&#8217;m also running a <a href="https://thepythoncodingstack.substack.com/p/when-it-works-is-not-good-enough">two-hour workshop this Thursday, 26 February, called </a><em><a href="https://thepythoncodingstack.substack.com/p/when-it-works-is-not-good-enough">Python&#8217;s Plumbing &#8226; Dunder Methods and Python&#8217;s Hidden Interface</a></em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://thepythoncodingstack.substack.com/p/when-it-works-is-not-good-enough&quot;,&quot;text&quot;:&quot;Book Workshops&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://thepythoncodingstack.substack.com/p/when-it-works-is-not-good-enough"><span>Book Workshops</span></a></p><p>Many operations you take for granted in your code are governed by special methods. Each class defines the special methods it needs, and Python knows how to handle instances of that class through those methods.</p><p>But let&#8217;s talk about different ways we refer to these special methods.</p><h2><strong>What&#8217;s In A Name?</strong></h2><p>These methods are called <em>special</em> methods. That&#8217;s their official name. These special methods have double underscores at the beginning and end of their names, such as <code>.__init__()</code>, <code>.__str__()</code>, and <code>.__iter__()</code>. This double-underscore notation led to the informal name &#8220;dunder methods&#8221;. And let&#8217;s face it, most people call them &#8220;dunder methods&#8221; instead of their actual name: &#8220;special methods.&#8221;</p><p>I often call them dunder methods, too. However, the term <em>dunder</em> merely describes the syntax, <em>d</em>ouble <em>under</em>score, so it doesn&#8217;t tell us much about what they do. The term <em>special</em> doesn&#8217;t tell us what they do, either, but it shows us they have a special role in Python.</p><h2><strong>There&#8217;s No Such Thing as Magic</strong></h2><p>Some people also call them <em>magic</em> methods. However, I avoid this term, and I discourage students from using it. It makes these methods look unnecessarily mysterious, perhaps difficult to understand because it&#8217;s all down to <em>magic</em>.</p><p>But there&#8217;s no such thing as magic (unless you&#8217;re Harry Potter). And the magic tricks we see from real-world &#8220;magicians&#8221; are just that &#8211; tricks. The magic dissolves away once you know how the trick works.</p><p>And if you&#8217;re learning Python, then you need to learn how to be a magician. You need to learn the &#8220;magic tricks.&#8221; Therefore, they&#8217;re no longer magic!</p><h2><strong>Python&#8217;s Plumbing (&#8221;Plumbing Methods&#8221;, Anyone?)</strong></h2><p>So, &#8220;special method&#8221; tells us that these methods are important, but it doesn&#8217;t tell us what they do. &#8220;Dunder method&#8221; describes the syntax. &#8220;Magic method&#8221; misleads us and doesn&#8217;t provide any useful insight.</p><p>How about &#8220;plumbing methods&#8221; then? Now, before anyone takes me too seriously, I&#8217;m saying this with my tongue firmly in my cheek. I&#8217;m not that foolish to suggest a new term for the whole Python community to adopt. And it&#8217;s not as flashy as &#8220;magic methods&#8221; or as cool as &#8220;dunder methods&#8221;. But bear with me&#8230;</p><p>Let&#8217;s explore the analogy, even if the term won&#8217;t catch on.</p><p><em>Disclaimer: I know very little about plumbing. But I think that&#8217;s OK for this essay!</em></p><p>There are pipes, valves, and other stuff carrying water (clean or otherwise) around your house. You know they&#8217;re there. You need them there. But you don&#8217;t see them.</p><p>You don&#8217;t think about these pipes unless you&#8217;re building the house &#8211; or unless the pipes are blocked or leaking.</p><p>The house&#8217;s plumbing keeps things running smoothly. Yet, it&#8217;s out of sight, and you don&#8217;t normally think about it. You take it for granted.</p><p>You see where I&#8217;m going, right?</p><p>Python&#8217;s special methods perform the same role. You don&#8217;t normally see them when coding since they&#8217;re called implicitly, behind the scenes. You do need to define the special methods you need when you&#8217;re defining the class, just like you&#8217;ll need to lay the pipes when building or modifying your house.</p><p>And if something goes wrong in your code, you may need to dive into how these dunder methods behave, just as when you have a leak and need to explore which pipe is responsible.</p><p>Good plumbing is reliable, predictable. Bad plumbing is asking for trouble. The same applies to the infrastructure you create through the special methods you define in classes.</p><p>So, there you go, they&#8217;re &#8220;plumbing methods&#8221;. This name tells us what they do!</p><div><hr></div><p>I&#8217;m running a <strong>two-hour live workshop this Thursday called </strong><em><strong><a href="https://thepythoncodingstack.substack.com/p/when-it-works-is-not-good-enough">Python&#8217;s Plumbing &#8226; Dunder Methods and Python&#8217;s Hidden Interface</a></strong></em><strong>.</strong> This is your last chance to join and I may not run this workshop again for a while.</p><p>This workshop is the first of three in the <em>Python Behind the Scenes</em> series. The other two workshops in the series are:</p><ul><li><p>#2 &#8226; <strong>Pythonic Iteration:</strong> Iterables, Iterators, <code>itertools</code></p></li><li><p>#3 &#8226; <strong>To Inherit or Not?</strong> Inheritance, Composition, Abstract Base Classes, and Protocols</p></li></ul><p>Join all three, or pick and choose:</p><div class="embedded-post-wrap" data-attrs="{&quot;id&quot;:188398348,&quot;url&quot;:&quot;https://www.thepythoncodingstack.com/p/when-it-works-is-not-good-enough&quot;,&quot;publication_id&quot;:1563052,&quot;embedding_publication_id&quot;:null,&quot;publication_name&quot;:&quot;The Python Coding Stack&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!Dn3k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4a59e8-e362-456b-8427-934e87c31a0d_600x600.png&quot;,&quot;title&quot;:&quot;When \&quot;It Works\&quot; Is Not Good Enough &#8226; Live Workshops&quot;,&quot;truncated_body_text&quot;:&quot;You&#8217;ve been reading articles here on The Python Coding Stack. How about live workshops?&quot;,&quot;date&quot;:&quot;2026-02-18T17:13:03.909Z&quot;,&quot;like_count&quot;:1,&quot;comment_count&quot;:2,&quot;bylines&quot;:[{&quot;id&quot;:120170782,&quot;name&quot;:&quot;Stephen Gruppetta&quot;,&quot;handle&quot;:&quot;stephengruppetta&quot;,&quot;previous_name&quot;:null,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ca736a83-f5a1-4563-ac6c-c09a9e6fa351_800x800.png&quot;,&quot;bio&quot;:&quot;Constantly looking for simple ways to explain complex things in Python &#8226; You'll find Python and narrative technical writing stuff here&#8230; stephengruppetta.com&quot;,&quot;profile_set_up_at&quot;:&quot;2023-04-08T06:55:32.227Z&quot;,&quot;reader_installed_at&quot;:&quot;2023-04-08T20:05:07.058Z&quot;,&quot;publicationUsers&quot;:[{&quot;id&quot;:1532734,&quot;user_id&quot;:120170782,&quot;publication_id&quot;:1563052,&quot;role&quot;:&quot;admin&quot;,&quot;public&quot;:true,&quot;is_primary&quot;:true,&quot;publication&quot;:{&quot;id&quot;:1563052,&quot;name&quot;:&quot;The Python Coding Stack&quot;,&quot;subdomain&quot;:&quot;thepythoncodingstack&quot;,&quot;custom_domain&quot;:&quot;www.thepythoncodingstack.com&quot;,&quot;custom_domain_optional&quot;:false,&quot;hero_text&quot;:&quot;I write the articles I wish I had when I was learning Python programming\n\nI learn through narratives, stories. And I communicate in the same way, with a friendly and relaxed tone, clear and accessible&quot;,&quot;logo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ab4a59e8-e362-456b-8427-934e87c31a0d_600x600.png&quot;,&quot;author_id&quot;:120170782,&quot;primary_user_id&quot;:120170782,&quot;theme_var_background_pop&quot;:&quot;#D10000&quot;,&quot;created_at&quot;:&quot;2023-04-08T06:55:58.228Z&quot;,&quot;email_from_name&quot;:&quot;Stephen Gruppetta from The Python Coding Stack&quot;,&quot;copyright&quot;:&quot;Stephen Gruppetta&quot;,&quot;founding_plan_name&quot;:&quot;Supporting Member&quot;,&quot;community_enabled&quot;:true,&quot;invite_only&quot;:false,&quot;payments_state&quot;:&quot;enabled&quot;,&quot;language&quot;:null,&quot;explicit&quot;:false,&quot;homepage_type&quot;:&quot;magaziney&quot;,&quot;is_personal_mode&quot;:false}},{&quot;id&quot;:1721978,&quot;user_id&quot;:120170782,&quot;publication_id&quot;:1741371,&quot;role&quot;:&quot;admin&quot;,&quot;public&quot;:true,&quot;is_primary&quot;:false,&quot;publication&quot;:{&quot;id&quot;:1741371,&quot;name&quot;:&quot;Breaking the Rules&quot;,&quot;subdomain&quot;:&quot;breakingtherules&quot;,&quot;custom_domain&quot;:null,&quot;custom_domain_optional&quot;:false,&quot;hero_text&quot;:&quot;A story about telling stories in technical writing.\n\nThis substack is about my journey exploring Narrative Technical Writing.\n\nIt's about breaking the traditional rules and borrowing techniques from storytelling to make compelling technical articles&quot;,&quot;logo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/957ad311-5542-40a5-9501-ade43452a524_280x280.png&quot;,&quot;author_id&quot;:120170782,&quot;primary_user_id&quot;:null,&quot;theme_var_background_pop&quot;:&quot;#FF9900&quot;,&quot;created_at&quot;:&quot;2023-06-18T19:22:23.910Z&quot;,&quot;email_from_name&quot;:null,&quot;copyright&quot;:&quot;Stephen Gruppetta&quot;,&quot;founding_plan_name&quot;:&quot;Founding Member&quot;,&quot;community_enabled&quot;:true,&quot;invite_only&quot;:false,&quot;payments_state&quot;:&quot;disabled&quot;,&quot;language&quot;:null,&quot;explicit&quot;:false,&quot;homepage_type&quot;:&quot;magaziney&quot;,&quot;is_personal_mode&quot;:false}},{&quot;id&quot;:5098065,&quot;user_id&quot;:120170782,&quot;publication_id&quot;:4997978,&quot;role&quot;:&quot;admin&quot;,&quot;public&quot;:true,&quot;is_primary&quot;:false,&quot;publication&quot;:{&quot;id&quot;:4997978,&quot;name&quot;:&quot;Back on the Track&quot;,&quot;subdomain&quot;:&quot;backonthetrack&quot;,&quot;custom_domain&quot;:null,&quot;custom_domain_optional&quot;:false,&quot;hero_text&quot;:&quot;My journey as I return back to track and field athletics after a 25-year gap&#8230; as a masters athlete (translation: old) and an aspiring coach&quot;,&quot;logo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9cd864f5-2da4-47ae-8d64-51aec2fa1f85_550x550.png&quot;,&quot;author_id&quot;:120170782,&quot;primary_user_id&quot;:null,&quot;theme_var_background_pop&quot;:&quot;#FF6719&quot;,&quot;created_at&quot;:&quot;2025-05-12T17:03:22.009Z&quot;,&quot;email_from_name&quot;:null,&quot;copyright&quot;:&quot;Stephen Gruppetta&quot;,&quot;founding_plan_name&quot;:null,&quot;community_enabled&quot;:true,&quot;invite_only&quot;:false,&quot;payments_state&quot;:&quot;disabled&quot;,&quot;language&quot;:null,&quot;explicit&quot;:false,&quot;homepage_type&quot;:&quot;magaziney&quot;,&quot;is_personal_mode&quot;:false}}],&quot;twitter_screen_name&quot;:&quot;s_gruppetta_ct&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null,&quot;status&quot;:{&quot;bestsellerTier&quot;:null,&quot;subscriberTier&quot;:1,&quot;leaderboard&quot;:null,&quot;vip&quot;:false,&quot;badge&quot;:{&quot;type&quot;:&quot;subscriber&quot;,&quot;tier&quot;:1,&quot;accent_colors&quot;:null},&quot;paidPublicationIds&quot;:[1717194],&quot;subscriber&quot;:null}}],&quot;utm_campaign&quot;:null,&quot;belowTheFold&quot;:true,&quot;type&quot;:&quot;newsletter&quot;,&quot;language&quot;:&quot;en&quot;,&quot;source&quot;:null}" data-component-name="EmbeddedPostToDOM"><a class="embedded-post" native="true" href="https://www.thepythoncodingstack.com/p/when-it-works-is-not-good-enough?utm_source=substack&amp;utm_campaign=post_embed&amp;utm_medium=web"><div class="embedded-post-header"><img class="embedded-post-publication-logo" src="https://substackcdn.com/image/fetch/$s_!Dn3k!,w_56,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4a59e8-e362-456b-8427-934e87c31a0d_600x600.png" loading="lazy"><span class="embedded-post-publication-name">The Python Coding Stack</span></div><div class="embedded-post-title-wrapper"><div class="embedded-post-title">When "It Works" Is Not Good Enough &#8226; Live Workshops</div></div><div class="embedded-post-body">You&#8217;ve been reading articles here on The Python Coding Stack. How about live workshops&#8230;</div><div class="embedded-post-cta-wrapper"><span class="embedded-post-cta">Read more</span></div><div class="embedded-post-meta">8 months ago &#183; 1 like &#183; 2 comments &#183; Stephen Gruppetta</div></a></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://thepythoncodingstack.substack.com/p/when-it-works-is-not-good-enough&quot;,&quot;text&quot;:&quot;Book Workshops&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://thepythoncodingstack.substack.com/p/when-it-works-is-not-good-enough"><span>Book Workshops</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gOVQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71567a3-256a-421f-9c79-eb68b4703ed7_1920x1380.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gOVQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71567a3-256a-421f-9c79-eb68b4703ed7_1920x1380.jpeg 424w, https://substackcdn.com/image/fetch/$s_!gOVQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71567a3-256a-421f-9c79-eb68b4703ed7_1920x1380.jpeg 848w, https://substackcdn.com/image/fetch/$s_!gOVQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71567a3-256a-421f-9c79-eb68b4703ed7_1920x1380.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!gOVQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71567a3-256a-421f-9c79-eb68b4703ed7_1920x1380.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!gOVQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71567a3-256a-421f-9c79-eb68b4703ed7_1920x1380.jpeg" width="628" height="451.59065934065933" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c71567a3-256a-421f-9c79-eb68b4703ed7_1920x1380.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1047,&quot;width&quot;:1456,&quot;resizeWidth&quot;:628,&quot;bytes&quot;:684833,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.thepythoncodingstack.com/i/189015669?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71567a3-256a-421f-9c79-eb68b4703ed7_1920x1380.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gOVQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71567a3-256a-421f-9c79-eb68b4703ed7_1920x1380.jpeg 424w, https://substackcdn.com/image/fetch/$s_!gOVQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71567a3-256a-421f-9c79-eb68b4703ed7_1920x1380.jpeg 848w, https://substackcdn.com/image/fetch/$s_!gOVQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71567a3-256a-421f-9c79-eb68b4703ed7_1920x1380.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!gOVQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71567a3-256a-421f-9c79-eb68b4703ed7_1920x1380.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Image by <a href="https://pixabay.com/users/thedigitalartist-202249/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=8012885">Pete Linforth</a> from <a href="https://pixabay.com//?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=8012885">Pixabay</a></em></p><div><hr></div><p><em>For more Python resources, you can also visit</em> <em><a href="https://realpython.com?utm_source=the-python-coding-stack">Real Python</a>&#8212;you may even stumble on one of my own articles or courses there!</em></p><p><em>Also, are you interested in technical writing? You&#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at</em> <em><a href="http://stephengruppetta.com/breaking-the-rules">Breaking the Rules</a></em>.</p><p><em>And you can find out more about me at</em> <em><a href="https://stephengruppetta.com?utm_source=the-python-coding-stack">stephengruppetta.com</a></em></p><p>Further reading related to this article&#8217;s topic:</p><ul><li><p><em>This is part of a longer OOP series:</em> <a href="https://www.thepythoncodingstack.com/p/python-oop-special-methods-6">Time for Something Special &#8226; Special Methods in Python Classes</a></p></li><li><p><a href="https://www.thepythoncodingstack.com/p/the-anatomy-of-a-for-loop">The Anatomy of a for Loop</a></p></li><li><p><a href="https://www.thepythoncodingstack.com/p/python-getitem-special-method-library-manorhouse">The Manor House, the Oak-Panelled Library, the Vending Machine, and Python&#8217;s __getitem__()  [Part 1]</a></p></li><li><p><a href="https://www.thepythoncodingstack.com/p/and-now-for-something-new-python-special-method">And Now for Something New</a></p></li><li><p><a href="https://thepythoncodingstack.substack.com/p/arithmetic-special-methods-python">Why Do 5 + &#8220;5&#8221; and &#8220;5&#8221; + 5 Give Different Errors in Python? &#8226; Do You Know The Whole Story?</a></p></li><li><p><a href="https://www.thepythoncodingstack.com/p/my-life-the-autobiography-of-a-python-object">My Life &#8226; The Autobiography of a Python Object</a></p></li><li><p><a href="https://www.thepythoncodingstack.com/p/telling-the-truthy-python-truthiness-falsiness">Telling The Truthy</a></p></li></ul>]]></content:encoded></item></channel></rss>