<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Vivis Dev</title>
    <description>The latest articles on Forem by Vivis Dev (@vivis_dev).</description>
    <link>https://forem.com/vivis_dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3491239%2F054782bc-311c-419e-8c43-2ec99af8e0d7.jpg</url>
      <title>Forem: Vivis Dev</title>
      <link>https://forem.com/vivis_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/vivis_dev"/>
    <language>en</language>
    <item>
      <title>Understanding Python dataclasses and how fields determine equality and hashing.</title>
      <dc:creator>Vivis Dev</dc:creator>
      <pubDate>Wed, 24 Sep 2025 12:01:00 +0000</pubDate>
      <link>https://forem.com/vivis_dev/understanding-python-dataclasses-and-how-fields-determine-equality-and-hashing-21jd</link>
      <guid>https://forem.com/vivis_dev/understanding-python-dataclasses-and-how-fields-determine-equality-and-hashing-21jd</guid>
      <description>&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!9qv7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35067486-bcc0-4e38-9e6b-52a47cfae099_1470x1538.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo39ykdiirnavsaq5re8o.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In monasteries bowls are often used for meditation rituals. Some are empty, others hold offerings: coins, water, and small tokens of care. In Python, dataclasses behave much like these bowls. &lt;/p&gt;

&lt;p&gt;Each dataclass instance is a bowl; the data it carries are the offerings inside. How Python evaluates equality and hashing depends on both the contents and the form of the bowl itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 1: What is a Dataclass?
&lt;/h3&gt;

&lt;p&gt;Dataclasses, introduced in Python 3.7, are a convenience feature. They save you from writing boilerplate code when you need simple classes that hold data. &lt;/p&gt;

&lt;p&gt;For example, without dataclasses you might write: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!5F9B!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cbae8d0-a4c0-49d6-8e4a-99637698c9e6_1470x814.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3nql2b71py2soyu9ifb.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And with a dataclass it becomes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!YkAl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc2edb06-eced-40a5-b2a8-a50357118b23_1470x560.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkslb4u96eglmqb52dzyt.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Python automatically creates the &lt;code&gt;__init__&lt;/code&gt;, &lt;code&gt;__eq__&lt;/code&gt;, and &lt;code&gt;__repr__&lt;/code&gt; functions for you when you use the dataclass decorator. &lt;/p&gt;

&lt;h3&gt;
  
  
  Part 2: The Empty Bowl
&lt;/h3&gt;

&lt;p&gt;Consider a bowl with no marked offerings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!OUI9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f7d793b-a10d-46d0-9315-cb432453fb40_1470x560.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsagnwoaoxpxa8jghf5os.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although values are assigned on the class, there are no annotated fields. When Python evaluates this bowl:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!1fBi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadd82696-e967-4684-89c2-c009a6f2d615_1470x390.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyuooy3sqhwi2e4n1l4c8.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Only attributes declared with annotations are considered the recognized contents of the bowl, contributing to equality and hash calculations. Adding type annotations transforms the bowl:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!EDHo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F695a6260-4426-4788-aa45-acf54753baa4_1470x474.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvm8of44s5rzk8os4lfnx.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the offerings are acknowledged, and hashing reflects their presence. Two bowls with identical annotated fields are equal, and their hashes correspond to their contents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 2: Comparing Different Bowls
&lt;/h3&gt;

&lt;p&gt;Next, imagine two bowls, each holding the same offerings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!58R5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a3e5dfc-b406-4e66-9e1d-7b1fbd1f257c_1470x688.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flmhb8aql7b3hmd1e9fqh.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The bowls may contain identical coins and water, yet they differ in form. Python compares both the contents and the type of the object:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!4nzY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82bb997b-08ce-4d8f-85cc-584a2b849880_1470x432.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1b79t2i3ocz02fnohs41.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Equality requires both the offerings and the vessel to match. Hashing, on the other hand, considers only the annotated fields.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 3: Fields vs Class Variables
&lt;/h3&gt;

&lt;p&gt;The distinction between annotated fields and class variables is crucial. Only annotated fields are considered offerings within the bowl. Class variables exist on the outside: they do not contribute to equality or hashing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!XUcj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feef8cce3-4a77-4219-96ca-b508ce92e118_1470x516.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft856ql4yniustz8eqmb3.png"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Part 4: Mutable vs Frozen Dataclasses
&lt;/h3&gt;

&lt;p&gt;You may have noticed that we are delcaring classes with &lt;code&gt;frozen=True.&lt;/code&gt;Dataclasses can be created &lt;strong&gt;mutable&lt;/strong&gt; or &lt;strong&gt;frozen&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mutable dataclasses&lt;/strong&gt; allow their contents to change after creation. This is the default.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Frozen dataclasses&lt;/strong&gt; make the contents immutable, like sealing the offerings inside a bowl.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!aCJO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03d024b3-a1d0-4c25-8854-5b6197e75e22_1470x688.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxtc4c0opf6iae0nowilh.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Frozenness also affects hashability. Only frozen dataclasses are hashable by default, because mutable bowls could change after being placed in a set or used as dict keys.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://pythonkoans.substack.com/p/koan-8-the-shifting-seal" rel="noopener noreferrer"&gt;Koan 8&lt;/a&gt; we learnt that the hashability of a class is determined by the implementation of the &lt;code&gt;__hash__&lt;/code&gt;function. When a dataclass is declared frozen, Python automatically creates a &lt;code&gt;__hash__&lt;/code&gt;function for you.&lt;/p&gt;




&lt;h3&gt;
  
  
  Part 5: Default Values and Factories
&lt;/h3&gt;

&lt;p&gt;Default values make bowls easier to create without specifying every offering:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!nzzZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6c8f8b1-2379-4dd1-a60f-d7064c3b3dc3_1470x474.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwwscvt1hqrax8xbtmgq0.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For mutable fields like lists or dictionaries, a &lt;strong&gt;default factory&lt;/strong&gt; is necessary. Using a default argument with a mutable object can lead to &lt;strong&gt;shared state&lt;/strong&gt; between instances:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!fpOK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8e3fb0e-ab75-43ad-9bf2-0f60a51bb9ec_1470x474.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6o365tabn8e5no0yeiiy.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is because default arguments are evaluated once at definition time, not execution time, as we learnt in &lt;a href="https://pythonkoans.substack.com/p/koan-3-the-ringing-bell" rel="noopener noreferrer"&gt;Koan 3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instead, a &lt;code&gt;default_factory&lt;/code&gt; creates a &lt;strong&gt;new object for each instance&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!tK_p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F056f3849-5874-4770-855a-8dad0a50c996_1470x516.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ksxchvftrqg2dqt20c5.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now each bowl has its own independent list of offerings. Python calls the factory function at the time the instance is created, ensuring the contents of each bowl remain separate.&lt;/p&gt;

&lt;p&gt;This distinction prevents subtle bugs and preserves the integrity of each bowl’s contents.&lt;/p&gt;




&lt;h3&gt;
  
  
  Offering the Bowl
&lt;/h3&gt;

&lt;p&gt;A bowl without marked offerings is indistinguishable from another empty bowl of the same type. Two bowls may contain identical offerings, yet remain separate if their forms differ. Hashing depends only on the contents, while equality considers both the contents and the form.&lt;/p&gt;

&lt;p&gt;Through careful attention to fields, mutability, defaults, and the auto-generated methods, dataclasses provide a simple, predictable structure for Python data objects.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pythonkoans.substack.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21qoUC%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fpythonkoans.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D-1479521753%2526version%253D9" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pythonkoans.substack.com/" rel="noopener noreferrer" class="c-link"&gt;
            Python Koans | Vivis Dev | Substack
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Python lessons wrapped in koans. Small puzzles, deep truths. Not your usual tutorial thread. Click to read Python Koans, by Vivis Dev, a Substack publication with hundreds of subscribers.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%213kVI%21%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F7ff23720-c183-45a5-bec1-55293cc5a7f6%252Ffavicon.ico"&gt;
          pythonkoans.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Exploring the dangerous power of unquoted Python strings, and how they caused CVE-2024-9287</title>
      <dc:creator>Vivis Dev</dc:creator>
      <pubDate>Sat, 20 Sep 2025 13:26:00 +0000</pubDate>
      <link>https://forem.com/vivis_dev/exploring-the-dangerous-power-of-unquoted-python-strings-and-how-they-caused-cve-2024-9287-1g7o</link>
      <guid>https://forem.com/vivis_dev/exploring-the-dangerous-power-of-unquoted-python-strings-and-how-they-caused-cve-2024-9287-1g7o</guid>
      <description>&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!vTd7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dee58aa-3407-4e39-8701-e68e92db04a1_1536x1186.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsrn44s6ejqfn4r86hwk.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Peril of Unquoted Arguments&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We often have the need to run commands in _*&lt;em&gt;shells1 *&lt;/em&gt;_using Python. &lt;a href="https://docs.python.org/3/library/subprocess.html" rel="noopener noreferrer"&gt;Subprocess&lt;/a&gt; is a cross-platform2 python module which helps you do this.&lt;/p&gt;

&lt;p&gt;Shells commands are separated by &lt;em&gt;&lt;strong&gt;command delimiters&lt;/strong&gt;&lt;/em&gt; (such as &lt;code&gt;‘;’&lt;/code&gt; &lt;code&gt;‘&amp;amp;&amp;amp;’&lt;/code&gt; &lt;code&gt;‘||’&lt;/code&gt; and &lt;code&gt;‘\n’&lt;/code&gt; in POSIX3&lt;code&gt;)&lt;/code&gt;. &lt;em&gt;&lt;strong&gt;Argument delimiters&lt;/strong&gt;&lt;/em&gt; on the other hand define how a single command’s arguments are split. On POSIX-compliant systems the &lt;code&gt;IFS&lt;/code&gt;4&lt;code&gt;&lt;/code&gt;environment variable defines the characters used to split arguments. By default, IFS is set to split on spaces, newlines and tabs.&lt;/p&gt;

&lt;p&gt;When a path or argument to a command contains a space, the shell does not see a single continuous entity. It sees two distinct things. A single path has become two arguments.&lt;/p&gt;

&lt;p&gt;Let us begin with a simple example.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 1: The Space as a Separator&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Consider the creation of a directory with a space in its name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!WQvo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F565e57f6-1a96-4f58-b60f-320daecf26d9_1536x624.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyh5uqr7aiz8idfjbccds.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we use the &lt;code&gt;os&lt;/code&gt; module it understands the path as a single string because it does not involve the shell's interpretation. The directory is created as intended with a space in its name.&lt;/p&gt;

&lt;p&gt;Now let us use the &lt;code&gt;subprocess&lt;/code&gt; module with the &lt;code&gt;shell=True&lt;/code&gt; flag. This flag instructs Python to pass our command to the shell for execution as a single string.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!geRb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccca0d7c-913a-43b7-941d-56217aa924d7_1536x624.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flykjtbfcjzopo670ep6n.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When this code runs, the shell sees the command as &lt;code&gt;mkdir my new path&lt;/code&gt;. The shell interprets this as a command to create a directory named &lt;code&gt;my&lt;/code&gt; a second directory named &lt;code&gt;new&lt;/code&gt; and a third named &lt;code&gt;path&lt;/code&gt;. The single path becomes three paths.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 2: The Command Injection&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The danger is not only in misinterpretation but also in &lt;strong&gt;malicious injection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Consider if the path came from an external source such as a user defined argument for example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!2JNI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faec13524-19af-4a6b-b52a-d6d43fd88054_1536x624.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2iha6gvhsya3bbbh66v.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The shell sees &lt;code&gt;mkdir my; rm -rf /&lt;/code&gt;. The semicolon is a command separator in the shell. The shell will first execute &lt;code&gt;mkdir my&lt;/code&gt; and then it will execute &lt;code&gt;rm -rf /&lt;/code&gt; which deletes the root directory.&lt;/p&gt;

&lt;p&gt;The unquoted path has allowed the user to inject a new command. This is a profound and dangerous failure of boundary. A simple space or semicolon can shatter the integrity of the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 3: The Principle of Quoting&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To prevent this we must use &lt;em&gt;&lt;strong&gt;quoting&lt;/strong&gt;&lt;/em&gt;. Quoting places a protective barrier around the string telling the shell to treat it as a single unit regardless of its contents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!Rgis!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9dd6da37-edf9-4414-b21c-c7df19e2643a_1536x624.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpp3ekfbcl18232bkv2k.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The shell now sees &lt;code&gt;mkdir "my; rm -rf /"&lt;/code&gt;. The entire string is treated as a single argument for &lt;code&gt;mkdir&lt;/code&gt;. No new directories are created. No commands are executed. The semicolon is rendered harmless a mere character within the string.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 4: The Path of Wisdom&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The wise path is the one that avoids the shell entirely when not needed. Most linters (like &lt;a href="https://docs.astral.sh/ruff/" rel="noopener noreferrer"&gt;ruff&lt;/a&gt;) will &lt;a href="https://docs.astral.sh/ruff/rules/subprocess-popen-with-shell-equals-true/" rel="noopener noreferrer"&gt;detect this&lt;/a&gt; for you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!ybL2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bbf0494-65b4-4a87-8f85-c740cf5527ec_1536x624.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9khqxqx1kmjrsr2u8yet.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we pass a list of arguments to &lt;code&gt;subprocess.run&lt;/code&gt;. Python does not pass a single string to the shell. Instead it executes &lt;code&gt;mkdir&lt;/code&gt; directly as a separate process and passes &lt;code&gt;user_input&lt;/code&gt; as its first argument. The shell is never involved and the risk is eliminated.&lt;/p&gt;

&lt;p&gt;This is the preferred way. It is clean and safe.&lt;/p&gt;

&lt;h4&gt;
  
  
  Part 5: A Real-World Command Injection (CVE-2024-9287)
&lt;/h4&gt;

&lt;p&gt;Command Injection is not just a theoretical problem. Last year, a &lt;a href="https://www.cvedetails.com/cve/CVE-2024-9287/" rel="noopener noreferrer"&gt;high severity vulnerability&lt;/a&gt; was found to affect all Python versions &amp;lt;= 3.13:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A vulnerability has been found in the CPython &lt;code&gt;venv\&lt;/code&gt; module and CLI where path names provided when creating a virtual environment were not quoted properly, allowing the creator to inject commands into virtual environment "activation" scripts (ie "source venv/bin/activate"). This means that attacker-controlled virtual environments are able to run commands when the virtual environment is activated. Virtual environments which are not created by an attacker or which aren't activated before being used (ie "./venv/bin/python") are not affected.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Previously, when a virtual environment was created, the activation scripts (&lt;code&gt;activate&lt;/code&gt;, &lt;code&gt;activate.bat&lt;/code&gt;, etc.) would use the environment name provided by the user to construct the &lt;code&gt;venv&lt;/code&gt; path without enclosing it in quotation marks. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!memv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1ecf6dd-6e0e-49ca-a8d0-09862e75451c_1536x766.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd410s3msxiheyes2u7nb.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, a name like &lt;code&gt;my test venv&lt;/code&gt; would be written into the script as &lt;code&gt;/home/user/my test venv&lt;/code&gt;. An attacker could craft a virtual environment name like &lt;code&gt;my-venv-with-space-and-command; malicious_command&lt;/code&gt; which would be interpreted by the shell as a path followed by an additional command to execute.&lt;/p&gt;

&lt;p&gt;The fix5 (see summarized code below) was to ensure that all paths written into the &lt;code&gt;venv&lt;/code&gt; activation scripts are properly &lt;em&gt;&lt;strong&gt;quoted&lt;/strong&gt;&lt;/em&gt; using the &lt;code&gt;shlex&lt;/code&gt; module. The fix uses &lt;code&gt;shlex.quote&lt;/code&gt; to ensure that any special characters or spaces in the path are escaped or enclosed in single quotes. This prevents the shell from misinterpreting the path as separate arguments or commands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!ZZrN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff323da82-fd5c-4add-9a57-afb99f348a3a_1536x1424.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1oobng1pen5dqs3j9y4k.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Forging the Blade
&lt;/h4&gt;

&lt;p&gt;The Master Blacksmith’s lesson was simple. Just as a piece of metal may appear to be a single piece of steel, but splits into shards when struck by a hammer; unquoted command strings are split into different arguments or commands by the shell.&lt;/p&gt;

&lt;p&gt;When constructing commands in Python:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Avoid &lt;code&gt;shell=True&lt;/code&gt; and use &lt;code&gt;subprocess.run([…])&lt;/code&gt; instead&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you must use &lt;code&gt;subprocess&lt;/code&gt; with &lt;code&gt;shell=True&lt;/code&gt;, quote the command string&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When constructing shell commands outside of &lt;code&gt;subprocess,&lt;/code&gt; use &lt;code&gt;shlex&lt;/code&gt; to avoid command injection from untrusted input.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The blacksmith does not strike a piece of steel without thought. So too should the developer not pass a path to the shell without care. &lt;/p&gt;

&lt;p&gt;Thanks for reading Python Koans! If you enjoyed this post, share it with your friends :)&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pythonkoans.substack.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21qoUC%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fpythonkoans.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D-1479521753%2526version%253D9" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pythonkoans.substack.com/" rel="noopener noreferrer" class="c-link"&gt;
            Python Koans | Vivis Dev | Substack
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Python lessons wrapped in koans. Small puzzles, deep truths. Not your usual tutorial thread. Click to read Python Koans, by Vivis Dev, a Substack publication with hundreds of subscribers.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%213kVI%21%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F7ff23720-c183-45a5-bec1-55293cc5a7f6%252Ffavicon.ico"&gt;
          pythonkoans.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;1&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;&lt;strong&gt;shell&lt;/strong&gt; _is a program that provides an interface between the user and the operating system (OS). It’s called a _“shell”&lt;/em&gt; because it surrounds the kernel (the “core”) and lets you interact with it. The shell takes &lt;em&gt;&lt;strong&gt;commands&lt;/strong&gt;&lt;/em&gt; (from your keyboard, a script, or another program), interprets them, and asks the OS to run the corresponding programs or built-in functions.&lt;/p&gt;

&lt;p&gt;2&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;subprocess&lt;/code&gt; module is available on all platforms &lt;em&gt;&lt;strong&gt;except&lt;/strong&gt;&lt;/em&gt; mobile (i.e. Android, iOS) and webassembly (i.e. WASI).&lt;/p&gt;

&lt;p&gt;3&lt;/p&gt;

&lt;p&gt;POSIX is an IEEE standard (IEEE 1003) defining a common API and shell behavior for Unix-like systems. It ensures that programs and scripts written on one POSIX-compliant system will work on another.&lt;/p&gt;

&lt;p&gt;4&lt;/p&gt;

&lt;p&gt;IFS can be made to split on other characters by changing it’s value before running a command. For example: &lt;code&gt;IFS=, read a b c &amp;lt;&amp;lt;&amp;lt; "one,two,three"; echo "$a | $b | $c"&lt;/code&gt; will split the comma-delimited string into the three variables a, b and c.&lt;/p&gt;

&lt;p&gt;5&lt;/p&gt;

&lt;p&gt;Because the issue affected all versions of Python, a patch was created for all 3.x versions. &lt;a href="https://github.com/python/cpython/commit/8450b2482586857d689b6658f08de9c8179af7db#diff-bcc6e36b2a636da9443a637b6c16d09c90225eedbf058cdaba308b8f963c859f" rel="noopener noreferrer"&gt;The commit diff for Python 3.12 can be found here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>shell</category>
      <category>cve</category>
    </item>
    <item>
      <title>Understanding how Python's list comprehensions work under the hood</title>
      <dc:creator>Vivis Dev</dc:creator>
      <pubDate>Sat, 20 Sep 2025 13:23:00 +0000</pubDate>
      <link>https://forem.com/vivis_dev/understanding-how-pythons-list-comprehensions-work-under-the-hood-1okc</link>
      <guid>https://forem.com/vivis_dev/understanding-how-pythons-list-comprehensions-work-under-the-hood-1okc</guid>
      <description>&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!6czw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89e338e2-8d8f-40ad-9d81-53a1374f5736_1536x1396.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwlnfipy6ivonsaeq97k.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  List Comprehensions
&lt;/h3&gt;

&lt;p&gt;List comprehensions are one of the most popular features in Python, and it allows us to write idiomatic (or Pythonic) code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!vwXR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F237e528e-743e-4792-a50a-8f12cc5dc971_1536x640.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5nriur9bqxwjs63oljh.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But, as the student discovered, although the end result may be the same in the above example, the process it took to arrive at the solution is very different.&lt;/p&gt;

&lt;p&gt;In fact, in Python 3.12, the process used to implement list comprehension changed significantly.&lt;/p&gt;

&lt;p&gt;Let us begin at the river.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 1: The Inner World of the For Loop&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://pythonkoans.substack.com/p/koan-4-the-grand-library" rel="noopener noreferrer"&gt;Koan 4&lt;/a&gt; we learnt that Python has 4 scopes (Local, Enclosing, Global and Builtin). However, there is also another “hidden” scope. Consider this loop:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!eGjv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4453cad-96b0-418a-813b-093dd5247930_1536x624.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm30q2v333e279nd5gaim.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The loop above reassigns the value of &lt;code&gt;x&lt;/code&gt; in the enclosing scope. After the loop runs, &lt;code&gt;x&lt;/code&gt; is no longer &lt;code&gt;10&lt;/code&gt;. Its value is now &lt;code&gt;2&lt;/code&gt;. The loop's variable &lt;code&gt;x&lt;/code&gt; "leaked" into the surrounding code.&lt;/p&gt;

&lt;p&gt;Now consider the same code implemented as a list comprehension:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!1Vrz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cfa8b8e-ada3-47d8-9ef3-9d828bba681e_1536x624.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9gx5cgqsp06wzhrnurrh.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This code prints &lt;code&gt;10&lt;/code&gt;. The &lt;code&gt;x&lt;/code&gt; in the comprehension is a new &lt;code&gt;x&lt;/code&gt;. It does not touch the outer &lt;code&gt;x&lt;/code&gt;. The list comprehension creates its own hidden scope.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 2: Peering into the water
&lt;/h3&gt;

&lt;p&gt;But how does Python ensure that list comprehension has a distinct scope? To observe what is happening, we must first gather some tools to help us peek inside the code.&lt;/p&gt;

&lt;p&gt;When you run a Python script, the CPython interpreter performs two main steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compilation:&lt;/strong&gt; The source code (the &lt;code&gt;.py&lt;/code&gt; file you wrote) is compiled into a stream of bytecode instructions. This bytecode is cached into a &lt;code&gt;.pyc&lt;/code&gt; file (pyc stands for "Python Compiled").&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt; The CPython virtual machine (a program written in C) reads and executes the bytecode instructions one by one. If the source file has changed, the bytecode is re-created, and if not, the cached &lt;code&gt;.pyc&lt;/code&gt; file is run directly&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Inspecting the bytecode can inform us about how the program is run by the interpreter. However, the bytecode is stored in an efficient binary format that is not human readable.&lt;/p&gt;

&lt;p&gt;To help us, we can use two functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;compile()&lt;/code&gt; to compile a python source code string into a &lt;code&gt;code&lt;/code&gt; object (also known as bytecode), with the &lt;a href="https://docs.python.org/3/library/functions.html#compile" rel="noopener noreferrer"&gt;following arguments&lt;/a&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;which&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;If&lt;/span&gt; &lt;span class="n"&gt;we&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;REPL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;then&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="nb"&gt;any&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="k"&gt;exec&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;multi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;statements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;single&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;statements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="n"&gt;single&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;single&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;interactive&lt;/span&gt; &lt;span class="n"&gt;statements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dis.dis&lt;/code&gt; to disassemble the bytecode and print it in a readable format&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!C290!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefb9e50-2b60-494f-9aa3-0abc88dffcc3_1536x808.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8mb8lvgyt7plid6r6cq.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The output of &lt;code&gt;dis.dis&lt;/code&gt; typically consists of several columns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Line Number:&lt;/strong&gt; This is the line number from the original Python source code. Note that a single line of Python code can compile into many bytecode instructions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Offset:&lt;/strong&gt; This is the byte offset of the instruction within the bytecode stream. It's used by jump instructions to navigate the code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Instruction:&lt;/strong&gt; This is the name of the bytecode instruction (e.g., &lt;code&gt;LOAD_CONST&lt;/code&gt;, &lt;code&gt;CALL_FUNCTION&lt;/code&gt;, &lt;code&gt;RETURN_VALUE&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Argument:&lt;/strong&gt; This is a value or a reference used by the instruction. It might be an index into a table of constants, variables, or names.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Argument Mnemonic:&lt;/strong&gt; This provides a human-readable name for the argument, making it easier to understand what the instruction is doing (e.g., &lt;code&gt;(range)&lt;/code&gt;, &lt;code&gt;(3)&lt;/code&gt;, &lt;code&gt;(None)&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We now have all the tools we need to peek inside the list comprehension, and to investigate why Python &amp;lt; 3.12 and Python &amp;gt;= 3.12, behave so differently.&lt;/p&gt;

&lt;p&gt;Until next time, may your stillness reveal the unseen flow.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;a href="https://pythonkoans.substack.com/p/koan-11-the-flowing-river-part-2?r=5yt43c" rel="noopener noreferrer"&gt;Continue to Part 2&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Thanks for reading Python Koans! If you enjoyed this post, feel free to share it.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pythonkoans.substack.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21qoUC%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fpythonkoans.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D-1479521753%2526version%253D9" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pythonkoans.substack.com/" rel="noopener noreferrer" class="c-link"&gt;
            Python Koans | Vivis Dev | Substack
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Python lessons wrapped in koans. Small puzzles, deep truths. Not your usual tutorial thread. Click to read Python Koans, by Vivis Dev, a Substack publication with hundreds of subscribers.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%213kVI%21%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F7ff23720-c183-45a5-bec1-55293cc5a7f6%252Ffavicon.ico"&gt;
          pythonkoans.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>python</category>
      <category>programming</category>
      <category>koan</category>
    </item>
    <item>
      <title>Exploring chained operations and order of evaluation in python expressions</title>
      <dc:creator>Vivis Dev</dc:creator>
      <pubDate>Sat, 20 Sep 2025 13:21:00 +0000</pubDate>
      <link>https://forem.com/vivis_dev/exploring-chained-operations-and-order-of-evaluation-in-python-expressions-534k</link>
      <guid>https://forem.com/vivis_dev/exploring-chained-operations-and-order-of-evaluation-in-python-expressions-534k</guid>
      <description>&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!_2Uu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc65e068b-3d9d-4715-a5af-7ca9e997c434_1470x1070.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdofmrl3nk1b6boc5qyv.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Hidden Teaching: The Flow of Assignment&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Just as a master potter knows that some steps must precede others regardless of the order they're written, a seasoned Python developer understands that a seemingly simple assignment is not a singular event. It's a flow. The value does not flow from left to right; it flows from &lt;strong&gt;right to left&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 1: Assignment Chains
&lt;/h3&gt;

&lt;p&gt;Consider a chain of assignments:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!m1fE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109e9171-cfa2-4cd6-96bf-852361644812_1470x390.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20a3ltv5irjz60s6qc00.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This may appear to assign &lt;code&gt;x + 2&lt;/code&gt;to &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, and &lt;code&gt;z&lt;/code&gt; simultaneously. But Python does not work that way. It evaluates this expression from &lt;strong&gt;right to left&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The expression &lt;code&gt;x + 2&lt;/code&gt; is evaluated. The value &lt;code&gt;12&lt;/code&gt; is assigned to the variable &lt;code&gt;z&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This assignment itself returns the value that was assigned, which is &lt;code&gt;12&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This returned value &lt;code&gt;12&lt;/code&gt; is then assigned to &lt;code&gt;y&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This new assignment also returns the value &lt;code&gt;12&lt;/code&gt;, which is then assigned to &lt;code&gt;x&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The operation is sequential. The rightmost value is the source and it moves leftward like water flowing down a stream. This is true for all assignment operators.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 2: Identity and Comparisons
&lt;/h3&gt;

&lt;p&gt;Consider the following assignment chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;list2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;list3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;list1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;On the surface, this appears simple. But because assignment chains are right-to-left, they create a single list object &lt;code&gt;[1,2,3]&lt;/code&gt; and assign a reference to that &lt;strong&gt;same object&lt;/strong&gt; to &lt;code&gt;list1&lt;/code&gt;, then to &lt;code&gt;list3&lt;/code&gt;, and finally to &lt;code&gt;list2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is not a copy. It's a &lt;em&gt;&lt;strong&gt;shared reference&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Modifying one of these variables will affect all others. We can confirm this by checking the identity of the objects using the &lt;code&gt;id()&lt;/code&gt;function. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!XB7j!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b867b53-5dc4-46ec-9ebc-4ed3ae77064e_1470x644.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhsmhpwuhjcuikdo68iti.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Part 3: Comparison Chains
&lt;/h3&gt;

&lt;p&gt;The same principle applies to chained comparison operators such as &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt; and &lt;code&gt;==&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;However, unlike the assignment operator, &lt;em&gt;&lt;strong&gt;a comparison chain is not evaluated from right to left&lt;/strong&gt;&lt;/em&gt;. The interpreter performs a series of &lt;em&gt;&lt;strong&gt;distinct comparisons&lt;/strong&gt;&lt;/em&gt; and joins them with an implicit &lt;code&gt;and&lt;/code&gt; operator.&lt;/p&gt;

&lt;p&gt;The expression &lt;code&gt;1 &amp;lt; x &amp;lt; 10&lt;/code&gt; is not shorthand for a single operation. It is shorthand for two separate operations:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is an important distinction. The value of &lt;code&gt;x&lt;/code&gt; is only evaluated once. The interpreter is smart enough to perform the minimal number of checks. If &lt;code&gt;1 &amp;lt; x&lt;/code&gt; is &lt;code&gt;False&lt;/code&gt;, the expression &lt;code&gt;x &amp;lt; 10&lt;/code&gt; is not evaluated at all because the entire &lt;code&gt;and&lt;/code&gt; expression will be &lt;code&gt;False&lt;/code&gt;. This is known as &lt;em&gt;&lt;strong&gt;short-circuiting&lt;/strong&gt;&lt;/em&gt; and &lt;em&gt;****&lt;/em&gt; you can use this to your advantage when writing logical expressions:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;high_probability_of_being_false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;low_probability_of_being_false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you write the expression with the higher likelihood of being &lt;code&gt;False&lt;/code&gt; first, then you avoid the second expression from being executed. This is something you might have to consider when writing performance sensitive code. &lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Part 4: Chained Comparisons with Side Effects&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;What about chained comparisons containing functions with side effects? While Python evaluates &lt;code&gt;a &amp;lt; b &amp;lt; c&lt;/code&gt; as &lt;code&gt;(a &amp;lt; b) and (b &amp;lt; c)&lt;/code&gt;, what happens if &lt;code&gt;b&lt;/code&gt; is the result of a function call? Consider the following example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!iMgq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7df5dea8-d110-47ae-9568-db4c8df3034d_1470x560.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxo6a00e0f2orqqd2mao.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The output will be &lt;code&gt;Getting B's value...&lt;/code&gt; followed by &lt;code&gt;The truth holds&lt;/code&gt;. The function &lt;code&gt;get_val_b&lt;/code&gt; is called &lt;em&gt;&lt;strong&gt;once&lt;/strong&gt;&lt;/em&gt; and its returned value &lt;code&gt;5&lt;/code&gt; is used for both &lt;code&gt;1 &amp;lt; 5&lt;/code&gt; and &lt;code&gt;5 &amp;lt; 10&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The comparison is evaluated as a logical &lt;code&gt;and&lt;/code&gt;, but the function call that produces the shared value for both comparisons is only executed once. &lt;/p&gt;
&lt;h3&gt;
  
  
  Firing the pot
&lt;/h3&gt;

&lt;p&gt;The master potter’s lesson was simple. The order in which instructions are given are not the order in which they must be executed. In Python, chained instructions are executed according to the following rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Assignment chains are a &lt;strong&gt;sequential flow&lt;/strong&gt; of value from right to left.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Comparison chains are a &lt;strong&gt;logical union&lt;/strong&gt; of individual comparisons.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you chain operators you are not simply performing a single operation. You are creating a silent understanding of how values will flow and how logic will be judged.&lt;/p&gt;

&lt;p&gt;If you enjoyed reading this post, consider sharing it with your friends!&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pythonkoans.substack.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21qoUC%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fpythonkoans.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D-1479521753%2526version%253D9" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pythonkoans.substack.com/" rel="noopener noreferrer" class="c-link"&gt;
            Python Koans | Vivis Dev | Substack
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Python lessons wrapped in koans. Small puzzles, deep truths. Not your usual tutorial thread. Click to read Python Koans, by Vivis Dev, a Substack publication with hundreds of subscribers.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%213kVI%21%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F7ff23720-c183-45a5-bec1-55293cc5a7f6%252Ffavicon.ico"&gt;
          pythonkoans.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Understanding Python’s rules for hashing</title>
      <dc:creator>Vivis Dev</dc:creator>
      <pubDate>Sat, 20 Sep 2025 13:08:00 +0000</pubDate>
      <link>https://forem.com/vivis_dev/understanding-pythons-rules-for-hashing-3jl</link>
      <guid>https://forem.com/vivis_dev/understanding-pythons-rules-for-hashing-3jl</guid>
      <description>&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!vrex!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b17fd52-aa35-4fda-b6bd-f9e3ba502f8a_1470x870.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff1o3rh73vbhp7gkfdgy5.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we speak of hashing in Python, we are entering a realm where identity, equality, and performance converge. In &lt;a href="https://pythonkoans.substack.com/p/koan-1-the-empty-path" rel="noopener noreferrer"&gt;Koan 1&lt;/a&gt;, we learnt the difference between Identity and Equality. Hashing in Python uses equality rather than identity. It promises that two objects that are equal will share the same mark.&lt;/p&gt;

&lt;p&gt;But the promise is not perfect. &lt;/p&gt;

&lt;p&gt;The koan shows us two NaNs ("Not a Number"). They cannot be equal to each other, by mathematical rule. Yet their hashes are equal. How can two things be marked the same, yet not be the same? &lt;/p&gt;

&lt;p&gt;Let us examine the seals carefully. &lt;/p&gt;

&lt;h3&gt;
  
  
  Part 1: What is a Hash?
&lt;/h3&gt;

&lt;p&gt;A hash is a value produced by a function designed to uniquely represent an object. In Python, the built-in hash() function returns an integer, and it is used by dictionaries and sets to test membership. When you do: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!rFrq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0d800d-ae30-419b-830a-8c69bfa3147c_1470x530.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg45yu1nfgcpex1neanyw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Python does not search through all the keys each time. Instead, it computes a hash of &lt;code&gt;"key"&lt;/code&gt;, uses it to jump to the right location in memory, and then checks equality to confirm.&lt;/p&gt;

&lt;p&gt;So the rule is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;If&lt;/strong&gt;&lt;code&gt;a == b&lt;/code&gt;&lt;strong&gt;, then&lt;/strong&gt;&lt;code&gt;hash(a) == hash(b)&lt;/code&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;But the reverse is not guaranteed: &lt;strong&gt;if&lt;/strong&gt;&lt;code&gt;hash(a) == hash(b)&lt;/code&gt;&lt;strong&gt;,&lt;/strong&gt;&lt;code&gt;a&lt;/code&gt;&lt;strong&gt;and&lt;/strong&gt;&lt;code&gt;b&lt;/code&gt;&lt;strong&gt;may not be equal.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first rule is what allows dictionaries and sets to function correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 2: The Strange Case of NaN
&lt;/h3&gt;

&lt;p&gt;Mathematically, NaN is defined so that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nan&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nan&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# False
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;float accepts the strings “nan” and “inf” with an optional prefix “+” or “-” for Not a Number (NaN) and positive or negative infinity. (from &lt;a href="https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex" rel="noopener noreferrer"&gt;Python Docs&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is deliberate: NaN is not equal to anything, not even itself.&lt;/p&gt;

&lt;p&gt;Yet Python ensures:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nan&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nan&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Why? Because hash-based collections require a consistent contract: objects that are equal must share the same hash, but Python &lt;em&gt;&lt;strong&gt;does not forbid unequal objects from sharing a hash&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Part 3: Hashing and Equality in User-Defined Classes
&lt;/h3&gt;

&lt;p&gt;When you define your own class, you may decide what equality and hashing mean.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!AFOy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F234b92b1-6037-4785-b85c-068012e32890_1470x658.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi1b46lybsyy8rxqetp8p.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now two scrolls with the same text are considered equal, and they share the same hash:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!mble!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55675a36-7dc4-46e8-ba06-bc7372ae996a_1470x486.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfhhop2rhnkvkrwlefxj.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you override &lt;code&gt;__eq__&lt;/code&gt; but forget to also override &lt;code&gt;__hash__&lt;/code&gt;, Python will often make your object &lt;strong&gt;unhashable&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!O5GJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4db585b1-30b3-4af3-9aa2-9e3536c8d409_1470x486.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzafcsmy2avcspqtxotmb.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you define them inconsistently like returning different hashes for equal objects, your code may behave strangely or inefficiently.&lt;/p&gt;

&lt;p&gt;These are the guidelines for implementation from the python docs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If a class does not define an &lt;code&gt;__eq__()&lt;/code&gt; method it should not define a &lt;code&gt;__hash__()&lt;/code&gt; operation either&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;if it defines &lt;code&gt;__eq__()&lt;/code&gt; but not &lt;code&gt;__hash__()&lt;/code&gt;, its instances will not be usable as items in hashable collections. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If a class defines mutable objects and implements an &lt;code&gt;__eq__()&lt;/code&gt; method, it should not implement &lt;code&gt;__hash__()&lt;/code&gt;, since the implementation of &lt;a href="https://docs.python.org/3/glossary.html#term-hashable" rel="noopener noreferrer"&gt;hashable&lt;/a&gt; collections requires that a key’s hash value is immutable (if the object’s hash value changes, it will be in the wrong hash bucket).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Part 4: Other Gotchas
&lt;/h3&gt;
&lt;h4&gt;
  
  
  4.1 Equality Across Types
&lt;/h4&gt;

&lt;p&gt;Python strives for consistency across related types:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# True
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# True
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Despite being different types, the value of &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;1.0&lt;/code&gt; are equal, so their hashes are also equal.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;integer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# "integer"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;At first glance this is elegant, but it means different numeric types may collapse into the same bucket, even if their meanings diverge in your domain. In scientific or financial code, this subtle equality can introduce surprising behaviour if you intended to distinguish &lt;code&gt;int&lt;/code&gt; from &lt;code&gt;float&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  4.2 Immutable vs Mutable
&lt;/h4&gt;

&lt;p&gt;Hashes are meant to be stable. If the contents of an object could change after being placed in a dictionary or set, the mapping would fall apart.&lt;/p&gt;

&lt;p&gt;This is why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Immutable containers&lt;/strong&gt; (like tuples) are hashable — but only if all their contents are hashable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mutable containers&lt;/strong&gt; (like lists and dicts) are not hashable.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;  &lt;span class="c1"&gt;# Works
&lt;/span&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;     &lt;span class="c1"&gt;# TypeError
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The error is not arbitrary. It protects you from placing an unstable key into a dictionary. Imagine changing the list inside a tuple after it has been used as a key — the hash would no longer reflect the object’s state.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Final Seal
&lt;/h3&gt;

&lt;p&gt;The novice saw two NaNs and said: "The seals are the same, so the things must be the same." The master corrected him: "The ink is the same, but the seal is broken."&lt;/p&gt;

&lt;p&gt;Hashing is a symbol, not a proof. Equality and hashing may touch, but they are not bound. To see clearly, we must hold both the symbol, and the substance it points to.&lt;/p&gt;

&lt;p&gt;Thanks for reading Python Koans! If you enjoyed this post, consider sharing with your friends or subscribing below:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pythonkoans.substack.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21qoUC%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fpythonkoans.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D-1479521753%2526version%253D9" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pythonkoans.substack.com/" rel="noopener noreferrer" class="c-link"&gt;
            Python Koans | Vivis Dev | Substack
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Python lessons wrapped in koans. Small puzzles, deep truths. Not your usual tutorial thread. Click to read Python Koans, by Vivis Dev, a Substack publication with hundreds of subscribers.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%213kVI%21%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F7ff23720-c183-45a5-bec1-55293cc5a7f6%252Ffavicon.ico"&gt;
          pythonkoans.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Navigating Python's import system and namespace packages</title>
      <dc:creator>Vivis Dev</dc:creator>
      <pubDate>Sat, 20 Sep 2025 12:56:00 +0000</pubDate>
      <link>https://forem.com/vivis_dev/navigating-pythons-import-system-and-namespace-packages-36ml</link>
      <guid>https://forem.com/vivis_dev/navigating-pythons-import-system-and-namespace-packages-36ml</guid>
      <description>&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!wWx_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F091b392b-d3f7-4e8b-b2c1-332b8de5dc44_1470x1338.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqh0uot1ek4ckqxwbx0mw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Hidden Teaching: The Nature of the Path&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The student believes that a village must be a single, bounded place. But the master knows that a village can be a &lt;strong&gt;concept&lt;/strong&gt; , defined by its connections and the paths that lead to it.&lt;/p&gt;

&lt;p&gt;Thanks for reading Python Koans! If you enjoyed this post, consider sharing with your friends or subscribing below:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pythonkoans.substack.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21qoUC%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fpythonkoans.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D-1479521753%2526version%253D9" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pythonkoans.substack.com/" rel="noopener noreferrer" class="c-link"&gt;
            Python Koans | Vivis Dev | Substack
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Python lessons wrapped in koans. Small puzzles, deep truths. Not your usual tutorial thread. Click to read Python Koans, by Vivis Dev, a Substack publication with hundreds of subscribers.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%213kVI%21%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F7ff23720-c183-45a5-bec1-55293cc5a7f6%252Ffavicon.ico"&gt;
          pythonkoans.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;In Python, the import system follows a similar principle. You may think of packages as walled villages with a clear boundary. Yet the true nature of the path (&lt;code&gt;sys.path&lt;/code&gt;) allows for the creation of unburdened villages: namespace packages, where a single logical package is assembled from components scattered across the filesystem.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Part 1: The Walled Village and the Path&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The Python interpreter, when asked to &lt;code&gt;import&lt;/code&gt; a package, follows a simple rule: it searches along a predefined path. This path, known as &lt;code&gt;sys.path&lt;/code&gt;, is a list of directories.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;traditional package&lt;/strong&gt; , or a "walled village," is a directory containing an &lt;code&gt;__init__.py&lt;/code&gt; file. This file marks the directory as a package and provides its clear boundary. When you import this package, Python finds the directory on its path and all its contents are then accessible.&lt;/p&gt;

&lt;p&gt;Consider a simple structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!o-Ho!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F984d5350-0347-439c-9057-f4583073e8a0_1134x410.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr40g3tc1utzkjhnmaomc.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To use this, the directory containing &lt;code&gt;my_village&lt;/code&gt; must be on &lt;code&gt;sys.path&lt;/code&gt;. If you run Python from the directory above &lt;code&gt;my_village&lt;/code&gt;, it is on the path by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!afRA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F138dc7b0-857a-456c-a61b-7d5f4b69a7c7_1234x298.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5peeq5tbjox1w80ldwxa.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;__init__.py&lt;/code&gt; is the village gate; it tells the interpreter, "This is the start of the journey."&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 2: The Unseen Path and the Root of Confusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The monk's confusion was not about the village itself, but about the assumption of a single starting point. The same confusion arises when a package is not directly on the path.&lt;/p&gt;

&lt;p&gt;Consider the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!fh09!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9f2e239-ebaf-4329-b0a7-1b50635c7df9_1234x494.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9gcxnyyrgjeelar3rwhq.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you run Python from a location that contains both &lt;code&gt;project1&lt;/code&gt; and &lt;code&gt;project2&lt;/code&gt;, the &lt;code&gt;my_village&lt;/code&gt; directories are &lt;strong&gt;not&lt;/strong&gt; directly on &lt;code&gt;sys.path&lt;/code&gt;. The path only contains the top-level directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!6lHc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c51479f-4249-4e16-b2ef-3727079a4296_1470x410.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvj7hg0nlx5q0mramb0w4.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The interpreter's path is not a tangled vine that seeks out all nested directories. It is a straight road, and it only knows about &lt;code&gt;project1&lt;/code&gt; and &lt;code&gt;project2&lt;/code&gt;. It does not know to look inside them for other packages.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Part 3: The Unburdened Village: A Shared Destination&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The solution lies in understanding the path as a shared space. A &lt;strong&gt;namespace package&lt;/strong&gt; is created when multiple directories with the same name, but &lt;strong&gt;without an&lt;/strong&gt;&lt;code&gt;__init__.py&lt;/code&gt;&lt;strong&gt;file&lt;/strong&gt; , are all placed on &lt;code&gt;sys.path&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By adding the parent directories (&lt;code&gt;project1&lt;/code&gt; and &lt;code&gt;project2&lt;/code&gt;) to the path, you instruct Python to consider them as valid starting points for imports.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!UMqS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd042b286-0f94-4fe0-8e15-69e0d216c9d7_1470x708.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5hhw3vqq989wc991bxca.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The interpreter first looks on the path, finds &lt;code&gt;project1&lt;/code&gt;, and sees a &lt;code&gt;my_village&lt;/code&gt; directory inside. It then continues its search, finds &lt;code&gt;project2&lt;/code&gt;, and sees another &lt;code&gt;my_village&lt;/code&gt; directory. It then &lt;strong&gt;merges&lt;/strong&gt; these two directories into a single logical package, the "unburdened village."&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 4: The Path of the Confused Traveller&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;What if one part of the village has a wall and the other does not?&lt;/p&gt;

&lt;p&gt;Consider if &lt;code&gt;project1&lt;/code&gt;'s &lt;code&gt;my_village&lt;/code&gt; directory contains an &lt;code&gt;__init__.py&lt;/code&gt; file, but &lt;code&gt;project2&lt;/code&gt;'s does not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!yL38!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb04156a1-9308-41d1-81bf-c4086de677f4_1470x538.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8tkpdjtfe6glbenqm23c.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this scenario, &lt;code&gt;my_village&lt;/code&gt; is &lt;strong&gt;no longer a namespace package&lt;/strong&gt;. Python's import system will find the first &lt;code&gt;my_village&lt;/code&gt; directory on its search path (&lt;code&gt;sys.path&lt;/code&gt;) that contains an &lt;code&gt;__init__.py&lt;/code&gt; file, and it will treat that directory as the entire package.&lt;/p&gt;

&lt;p&gt;When you run &lt;code&gt;import my_village.temple&lt;/code&gt;, the interpreter will find the &lt;code&gt;my_village&lt;/code&gt; directory in &lt;code&gt;project1&lt;/code&gt;, but it will &lt;strong&gt;not&lt;/strong&gt; continue to search for other &lt;code&gt;my_village&lt;/code&gt; directories. It will only look for &lt;code&gt;temple.py&lt;/code&gt; inside &lt;code&gt;project1/my_village&lt;/code&gt;. The file will not be found, and you will get a &lt;code&gt;ModuleNotFoundError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The presence of the &lt;code&gt;__init__.py&lt;/code&gt; file effectively "closes the gate" on the search for other parts of the package, turning it back into a traditional, "walled" package.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 5: The Power of the Unburdened Village&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Why would one choose to build a village without walls?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extensibility&lt;/strong&gt; : Frameworks like &lt;code&gt;pytest&lt;/code&gt; and &lt;code&gt;zope&lt;/code&gt; use this pattern. The core library defines a namespace, for example &lt;code&gt;my_app.plugins&lt;/code&gt;. Developers can then create their own packages, like &lt;code&gt;my_app.plugins.my_feature&lt;/code&gt;, and the core application will automatically find and load them without any modification to the main codebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decomposition&lt;/strong&gt; : A large project can be broken into smaller, independent libraries that are developed and versioned separately but still function as a single logical package.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modularity&lt;/strong&gt; : It allows for a more distributed and modular architecture, where a project's components can be managed and installed from different locations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Leaving the Village&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The master's reply was a lesson in perspective. The village is not the directory itself but the &lt;em&gt;concept&lt;/em&gt; of a shared space. The path is not a single location but a collection of all the roads one can travel.&lt;/p&gt;

&lt;p&gt;Your confusion, like the monk's, arose from seeking a single physical truth. But Python's import system, like the master’s village, shows us that meaning can be constructed from disparate parts as long as you follow the right path.&lt;/p&gt;

&lt;p&gt;Thanks for reading Python Koans! If you enjoyed this post, consider sharing with your friends or subscribing below:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pythonkoans.substack.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21qoUC%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fpythonkoans.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D-1479521753%2526version%253D9" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pythonkoans.substack.com/" rel="noopener noreferrer" class="c-link"&gt;
            Python Koans | Vivis Dev | Substack
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Python lessons wrapped in koans. Small puzzles, deep truths. Not your usual tutorial thread. Click to read Python Koans, by Vivis Dev, a Substack publication with hundreds of subscribers.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%213kVI%21%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F7ff23720-c183-45a5-bec1-55293cc5a7f6%252Ffavicon.ico"&gt;
          pythonkoans.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Exploring how functions in Python are treated as first-class citizens, and the untapped potential they hold.</title>
      <dc:creator>Vivis Dev</dc:creator>
      <pubDate>Sat, 20 Sep 2025 12:48:00 +0000</pubDate>
      <link>https://forem.com/vivis_dev/exploring-how-functions-in-python-are-treated-as-first-class-citizens-and-the-untapped-potential-495c</link>
      <guid>https://forem.com/vivis_dev/exploring-how-functions-in-python-are-treated-as-first-class-citizens-and-the-untapped-potential-495c</guid>
      <description>&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!NhBB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F086fc16b-5abb-4b01-aa02-9f1e993744ca_1804x790.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3lwp3eko737u786w11r7.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Functions as First-Class Citizens in Python
&lt;/h3&gt;

&lt;p&gt;In Python, a &lt;strong&gt;function&lt;/strong&gt; is much like a harp. An object that holds potential, a set of instructions waiting to be invoked and produce a harmonious result. This design choice makes functions "&lt;strong&gt;first-class citizens&lt;/strong&gt; ," opening up a world of elegant and powerful programming paradigms.&lt;/p&gt;

&lt;p&gt;Let's begin with this fundamental understanding.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 1: The Harp’s Existence
&lt;/h3&gt;

&lt;p&gt;What does it mean for a function to be a "first-class citizen"? It means that functions can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Assigned to variables&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Passed as arguments&lt;/strong&gt; to other functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Returned as values&lt;/strong&gt; from other functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stored in data structures&lt;/strong&gt; (lists, dictionaries, etc.).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is distinct from languages where functions are merely blocks of code that can be called, but not manipulated like other data.&lt;/p&gt;

&lt;p&gt;Consider a simple instruction for making music with our harp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;play_note&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The harp plays the note: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In Python, &lt;code&gt;play_note&lt;/code&gt; isn't just a command; it's an object. You can prove this by assigning it to another variable, much like one might refer to the harp by a different name in an orchestra:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;my_instrument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;play_note&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;my_instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;C major&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, &lt;code&gt;my_instrument&lt;/code&gt; now refers to the same function object as &lt;code&gt;play_note&lt;/code&gt;. The harp exists, and we have a way to access its potential for sound, even if no note has yet been played.&lt;/p&gt;
&lt;h3&gt;
  
  
  Part 2: Passing the Harp to the Musician (Higher-Order Functions)
&lt;/h3&gt;

&lt;p&gt;One of the most powerful implications of functions being first-class is the ability to &lt;em&gt;&lt;strong&gt;pass them as arguments&lt;/strong&gt;&lt;/em&gt; to other functions. Functions that accept other functions as arguments are known as "&lt;em&gt;&lt;strong&gt;higher-order functions&lt;/strong&gt;&lt;/em&gt;." This is akin to handing our harp to a skilled musician, trusting them to play its strings when the moment is right.&lt;/p&gt;

&lt;p&gt;A common example is the &lt;code&gt;map()&lt;/code&gt; function, which applies a given function to all items in an input list and returns an iterator. Let's imagine we have a sequence of musical ideas, and we want to apply a transformation to each using our harp's capabilities:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;harmonize_note&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; with harmony&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;melody_notes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;C&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;G&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Am&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;F&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;harmonized_melody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;harmonize_note&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;melody_notes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;harmonized_melody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, &lt;code&gt;harmonize_note&lt;/code&gt; is passed as an argument to &lt;code&gt;map&lt;/code&gt;. &lt;code&gt;map&lt;/code&gt; doesn't know &lt;em&gt;what&lt;/em&gt; &lt;code&gt;harmonize_note&lt;/code&gt; does, only that it's a callable object it can apply to each element. It simply knows how to take a series of musical ideas and apply the "play and harmonize" instruction to each.&lt;/p&gt;
&lt;h3&gt;
  
  
  Part 3: The Unnamed Chord (Lambdas)
&lt;/h3&gt;

&lt;p&gt;For simple, one-off musical expressions, Python offers "&lt;em&gt;&lt;strong&gt;lambda expressions&lt;/strong&gt;&lt;/em&gt;." These are small, anonymous functions that can be defined in a single line. They are particularly useful when you need a function object for a short period, often as an argument to a higher-order function, like a quick flourish instead of a formal composition.&lt;/p&gt;

&lt;p&gt;Our koan's core, &lt;code&gt;lambda x: x + 1&lt;/code&gt;, is an example of an anonymous function. It exists, it holds potential, but it doesn't have a formal name.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Using a lambda with map to transpose notes:
&lt;/span&gt;&lt;span class="n"&gt;base_frequencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;440&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;494&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;523&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# A4, B4, C5 frequencies
&lt;/span&gt;&lt;span class="n"&gt;transposed_frequencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;freq&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_frequencies&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transposed_frequencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Lambdas are succinct and often improve readability for simple operations, avoiding the need to define a full &lt;code&gt;def&lt;/code&gt; function for a trivial task. They are like a quickly improvised chord on the harp.&lt;/p&gt;
&lt;h3&gt;
  
  
  Part 4: Plucking the String Immediately (Immediate Invocation)
&lt;/h3&gt;

&lt;p&gt;Just as we can assign a lambda to a variable and then play it, we can also play it immediately after it's defined.&lt;/p&gt;

&lt;p&gt;Consider our simple &lt;code&gt;lambda&lt;/code&gt; from the koan:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This defines a function object. It is the silent harp string. To make it produce a result, we need to "pluck" it by providing an argument. We typically do this by assigning it to a variable first, then calling that variable:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;increment_frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;increment_frequency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;However, because the &lt;code&gt;lambda&lt;/code&gt; expression itself &lt;em&gt;evaluates&lt;/em&gt; to a function object, you can immediately apply arguments to it, just like you would with any other function reference.&lt;/p&gt;

&lt;p&gt;The koan's code demonstrates this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, &lt;code&gt;(lambda x: x + 1)&lt;/code&gt; creates the anonymous function object (the harp string), and &lt;code&gt;(10)&lt;/code&gt; immediately calls that function with the argument &lt;code&gt;10&lt;/code&gt; (plucks the string). The &lt;strong&gt;parentheses&lt;/strong&gt; around the lambda expression are crucial here to ensure the lambda is fully defined before the invocation operator &lt;code&gt;()&lt;/code&gt; attempts to call it. Without them, Python would expect a function name to call. &lt;/p&gt;

&lt;p&gt;This pattern, sometimes seen as an Immediately Invoked Function Expression (IIFE), is less common in everyday Python than in JavaScript, but it is perfectly valid and can be seen in specialized contexts, often for creating self-contained scopes or for quick, single-use computations without cluttering the namespace.&lt;/p&gt;
&lt;h3&gt;
  
  
  Closing the Circle
&lt;/h3&gt;

&lt;p&gt;The silent harp holds all its potential for music within. Only when a string is plucked does its melody emerge. And sometimes, the note is played the very moment the string is tied.&lt;/p&gt;

&lt;p&gt;In Python, functions are not just commands; they are entities with identity, capable of being manipulated, stored, and passed around like any other piece of data.&lt;/p&gt;

&lt;p&gt;Thanks for reading Python Koans! If you enjoyed this post, consider sharing with your friends or subscribing below:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pythonkoans.substack.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21qoUC%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fpythonkoans.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D-1479521753%2526version%253D9" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pythonkoans.substack.com/" rel="noopener noreferrer" class="c-link"&gt;
            Python Koans | Vivis Dev | Substack
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Python lessons wrapped in koans. Small puzzles, deep truths. Not your usual tutorial thread. Click to read Python Koans, by Vivis Dev, a Substack publication with hundreds of subscribers.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%213kVI%21%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F7ff23720-c183-45a5-bec1-55293cc5a7f6%252Ffavicon.ico"&gt;
          pythonkoans.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>python</category>
      <category>programming</category>
      <category>koan</category>
    </item>
    <item>
      <title>Understanding Late Binding in Python Closures</title>
      <dc:creator>Vivis Dev</dc:creator>
      <pubDate>Sat, 20 Sep 2025 12:48:00 +0000</pubDate>
      <link>https://forem.com/vivis_dev/understanding-late-binding-in-python-closures-2e5j</link>
      <guid>https://forem.com/vivis_dev/understanding-late-binding-in-python-closures-2e5j</guid>
      <description>&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!gdHE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6beae50-4f0d-4571-a3f2-0579c23ba879_1888x1406.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxy4alipibut5bhdfg4j.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Python, a function is more than a list of commands, as we learnt in &lt;a href="https://pythonkoans.substack.com/p/koan-5-the-silent-harp" rel="noopener noreferrer"&gt;Koan 5&lt;/a&gt;. When a function is defined inside another, it becomes a &lt;strong&gt;closure&lt;/strong&gt; , a small piece of code that remembers its parent's environment. This memory is powerful, but it's not a picture of the past. It's more like a window. It shows you the world as it is &lt;em&gt;right now&lt;/em&gt; , not as it was when the window was first opened. This phenomenon is known as &lt;strong&gt;late binding&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's begin by observing the simplest expression of a closure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!t9Pu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddd3fe6a-19dd-4ac9-9550-37462862f385_1360x458.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpl5n4r2bhfhpfsacrf4p.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, &lt;code&gt;execute_commission&lt;/code&gt; is the closure. It remembers the &lt;code&gt;item&lt;/code&gt; variable from its parent function, &lt;code&gt;make_commission&lt;/code&gt;. When we call &lt;code&gt;draw_sun()&lt;/code&gt;, it returns "sun" as expected. The closure has a clear, singular memory of its purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 2: The Master's Three Commands
&lt;/h3&gt;

&lt;p&gt;Now, let's observe the lazy calligrapher's technique, as described in the koan. He waits until all instructions have been given before beginning his work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!nhq1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6e8f7de-d01f-4fd3-a973-5fa71accb8a6_1360x608.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzgvox6jwpraut4f6y1d3.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One might expect the output to be "sun," "moon," and then "cloud." However, the code will print "cloud" three times.&lt;/p&gt;

&lt;p&gt;This is the very essence of late binding. The &lt;code&gt;execute_commission&lt;/code&gt; function doesn't capture the &lt;em&gt;value&lt;/em&gt; of &lt;code&gt;instruction&lt;/code&gt; at the time it's created. Instead, it holds a reference to the &lt;strong&gt;variable&lt;/strong&gt; &lt;code&gt;instruction&lt;/code&gt; itself. By the time we finally get around to executing the first function in the list, the &lt;code&gt;for&lt;/code&gt; loop has already completed, and the variable &lt;code&gt;instruction&lt;/code&gt; has a final value: "cloud."&lt;/p&gt;

&lt;p&gt;All three closures refer to the same single &lt;code&gt;instruction&lt;/code&gt; variable in the outer scope, which has finished its journey. &lt;/p&gt;

&lt;h3&gt;
  
  
  Part 3: The Diligent Calligrapher
&lt;/h3&gt;

&lt;p&gt;To correct this behavior, we need a way for each closure to capture its own unique copy of the instruction at the moment it's created. We must teach the calligrapher to be diligent and remember each command individually.&lt;/p&gt;

&lt;p&gt;One method to achieve this is by using a &lt;strong&gt;default argument&lt;/strong&gt;. As we learnt in &lt;a href="https://pythonkoans.substack.com/p/koan-3-the-ringing-bell" rel="noopener noreferrer"&gt;Koan 3&lt;/a&gt;, default arguments are evaluated once, at the time the function is defined. We can modify our &lt;code&gt;make_commissions&lt;/code&gt; function to use this technique.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!duEq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9955247-72a4-442a-a01c-1718ad4a1e9d_1360x608.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgi7xs9dvn70468ocy8vz.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time, the output is as we first intended. By setting the default argument &lt;code&gt;item=instruction&lt;/code&gt;, we force Python to evaluate &lt;code&gt;instruction&lt;/code&gt; and bind its current value to the &lt;code&gt;item&lt;/code&gt; parameter for each new function. Each closure now holds its own unique copy of the instruction, rather than sharing a single reference.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 4: The Anonymous Calligrapher
&lt;/h3&gt;

&lt;p&gt;This principle of late binding also applies to &lt;strong&gt;lambda functions&lt;/strong&gt;. As we learnt in &lt;a href="https://pythonkoans.substack.com/p/koan-5-the-silent-harp" rel="noopener noreferrer"&gt;Koan 5&lt;/a&gt;, lambdas are simply anonymous functions. The "lazy calligrapher" effect is very common when lambdas are created in a loop.&lt;/p&gt;

&lt;p&gt;Here is the original problem, written with a lambda:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!TcIV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdddb2d67-aef5-4187-a8df-5f36c6b18482_1360x534.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdgy9eql5ms2853qkijmz.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just like with the &lt;code&gt;def&lt;/code&gt;-defined function, this will also return "cloud," because the lambda closes over the &lt;code&gt;instruction&lt;/code&gt; variable, not its value.&lt;/p&gt;

&lt;p&gt;To correct this, we use a default argument for the lambda function:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!WNyY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6671774a-d39f-4022-a655-701bff81aa91_1360x534.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxtpvxrmw3z7rj74vs24.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can make this more concise and &lt;em&gt;pythonic&lt;/em&gt; by using list comprehension to create all the commissions in a single stroke. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!eN59!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a9a6084-a62f-4cb1-a825-dcb753f437d8_1360x422.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr0mh6u868vpbw9vti6i9.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 5: The Master's Scrupulous Note
&lt;/h3&gt;

&lt;p&gt;What if we could not use a default argument? Is there another way to force the calligrapher to capture the instruction immediately? The solution lies in creating a new scope for each instruction, forcing each closure to "listen" to a unique voice.&lt;/p&gt;

&lt;p&gt;We can achieve this by creating a factory function that takes the instruction as an argument, thus creating a new, isolated environment for each closure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!P2k_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2b19c91-d217-4720-81f9-8b60a5972de3_1360x756.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdpm3kegzg2qg1xw7232c.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This code also yields the correct output. On each iteration of the loop, we call &lt;code&gt;create_executor(instruction)&lt;/code&gt;. This function is given the &lt;em&gt;current value&lt;/em&gt; of &lt;code&gt;instruction&lt;/code&gt;. It then creates a new closure, &lt;code&gt;execute_commission&lt;/code&gt;, which closes over &lt;code&gt;item&lt;/code&gt;. Because &lt;code&gt;item&lt;/code&gt; is a local variable within &lt;code&gt;create_executor&lt;/code&gt;, its value is isolated from the outer loop's changes (as we learnt in &lt;a href="https://pythonkoans.substack.com/p/koan-4-the-grand-library" rel="noopener noreferrer"&gt;Koan 4&lt;/a&gt; about Python’s LEGB rule). Each call to &lt;code&gt;create_executor&lt;/code&gt; creates a new, private memory for the closure it returns. It is as if the master wrote a small, specific note for each drawing, which the calligrapher could not forget.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 6: The Customized Brush
&lt;/h3&gt;

&lt;p&gt;There are other, more concise or functional ways to achieve the same result. The choice of method often comes down to the clarity and style you prefer. We can use &lt;code&gt;functools.partial&lt;/code&gt; to "pre-bind" the argument to a function. This is like preparing a special brush for each task, with the instruction already etched into its handle. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!VQjC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcda1562b-9a5f-426e-a119-2a9e0a44c3ab_1360x720.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ludl96g3f8zqjdyd4fn.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Final Brushstroke
&lt;/h3&gt;

&lt;p&gt;The Master's teaching was simple: the calligrapher did not remember what was once said, only the command that was last spoken when the drawing finally began.&lt;/p&gt;

&lt;p&gt;In your code, your functions are like the forgetful calligrapher. The closure does not remember the values the variable held when it was created, only the last value is recalled when the function is run.&lt;/p&gt;

&lt;p&gt;Thanks for reading Python Koans! If you enjoyed this post, consider sharing with your friends or subscribing below:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pythonkoans.substack.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21qoUC%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fpythonkoans.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D-1479521753%2526version%253D9" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pythonkoans.substack.com/" rel="noopener noreferrer" class="c-link"&gt;
            Python Koans | Vivis Dev | Substack
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Python lessons wrapped in koans. Small puzzles, deep truths. Not your usual tutorial thread. Click to read Python Koans, by Vivis Dev, a Substack publication with hundreds of subscribers.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%213kVI%21%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F7ff23720-c183-45a5-bec1-55293cc5a7f6%252Ffavicon.ico"&gt;
          pythonkoans.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>python</category>
      <category>programming</category>
      <category>koan</category>
    </item>
    <item>
      <title>Understanding Python’s LEGB rule, closures, and why variables sometimes behave like shadows.</title>
      <dc:creator>Vivis Dev</dc:creator>
      <pubDate>Sat, 20 Sep 2025 12:48:00 +0000</pubDate>
      <link>https://forem.com/vivis_dev/understanding-pythons-legb-rule-closures-and-why-variables-sometimes-behave-like-shadows-39n6</link>
      <guid>https://forem.com/vivis_dev/understanding-pythons-legb-rule-closures-and-why-variables-sometimes-behave-like-shadows-39n6</guid>
      <description>&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!ikfG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ddcc158-48eb-4a63-8d29-5c6af991a205_1670x1088.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0uipzv5z6acpwypfmuqw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Variable Scope and the Shadows They Cast
&lt;/h3&gt;

&lt;p&gt;In Python, variables are not always where they appear to be.&lt;br&gt;&lt;br&gt;
When a function runs, it carries with it not just its own code, but the shadow of names it has seen, names that belong to outer scopes.&lt;/p&gt;

&lt;p&gt;To understand this, we must learn to see how Python &lt;em&gt;resolves names&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let us begin simply.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Part 1: The Local Glow&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The simplest form of scope is the local scope. When you define a variable inside a function, it exists only within that function's execution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, world!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# This would cause an error: NameError
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, &lt;code&gt;message&lt;/code&gt; is born and dies within the &lt;code&gt;greet&lt;/code&gt; function. It's like a lamp lit only inside a small room; its light doesn't extend beyond the doorway.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Part 2: The Enclosing Room&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now, let's open another door. Python has a concept called enclosing scope. This applies to nested functions. If a variable isn't found in the immediate local scope, Python looks outwards to any enclosing functions.&lt;/p&gt;

&lt;p&gt;Consider this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;outer_function&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;outer_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;From the outer room.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inner_function&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outer_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;inner_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;outer_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When &lt;code&gt;inner_function&lt;/code&gt; is called, it first looks for &lt;code&gt;outer_message&lt;/code&gt; within its own scope. It doesn't find it. So, it looks in the scope of &lt;code&gt;outer_function&lt;/code&gt;, where &lt;code&gt;outer_message&lt;/code&gt; resides. This works. The inner function can see the variables of its enclosing function, like seeing the light of a lamp in an adjacent room through an open door.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Part 3: The Global Stage&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Beyond local and enclosing scopes, there is the global scope. Variables defined at the top level of a script or module are global. They can be accessed from anywhere within that module.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;global_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;From the wide world.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display_global&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;global_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;display_global&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, &lt;code&gt;display_global&lt;/code&gt; can access &lt;code&gt;global_message&lt;/code&gt; because it's in the global scope. This is like the sun's light, visible from every room.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Part 4: The Built-in Universe&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, there's the built-in scope. This contains all the names that Python pre-defines, such as &lt;code&gt;print&lt;/code&gt;, &lt;code&gt;len&lt;/code&gt;, &lt;code&gt;str&lt;/code&gt;, &lt;code&gt;True&lt;/code&gt;, &lt;code&gt;False&lt;/code&gt;, and &lt;code&gt;None&lt;/code&gt;. These are always available.&lt;/p&gt;

&lt;p&gt;The order in which Python searches for names is known as the LEGB rule:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;L&lt;/strong&gt; ocal (current function)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;E&lt;/strong&gt; nclosing function locals (from inner to outer enclosing functions)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;G&lt;/strong&gt; lobal (top-level of the module)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;B&lt;/strong&gt; uilt-in (Python's pre-defined names)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Python stops at the first place it finds the name.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Part 5: When Shadows Deceive - Variable Binding&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The true complexity arises when you &lt;em&gt;assign&lt;/em&gt; to a variable within a deeper scope. When Python encounters an assignment statement, it assumes you intend to create or modify a variable in the &lt;em&gt;current&lt;/em&gt; scope, unless explicitly told otherwise.&lt;/p&gt;

&lt;p&gt;Let's revisit our koan's example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;  &lt;span class="c1"&gt;# Global shadow
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;outer_lamp&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;  &lt;span class="c1"&gt;# This 'shadow' is local to outer_lamp()
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inner_lamp&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# This 'shadow' refers to outer_lamp()'s shadow
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;inner_lamp&lt;/span&gt;

&lt;span class="n"&gt;lamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;outer_lamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;lamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# Output: 20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here's the crucial part:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;shadow = 10&lt;/code&gt; establishes a global &lt;code&gt;shadow&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inside &lt;code&gt;outer_lamp()&lt;/code&gt;, &lt;code&gt;shadow = 20&lt;/code&gt; creates a &lt;em&gt;new&lt;/em&gt; , &lt;em&gt;local variable&lt;/em&gt; within &lt;code&gt;outer_lamp&lt;/code&gt;'s scope. This &lt;code&gt;shadow&lt;/code&gt; is entirely separate from the global &lt;code&gt;shadow&lt;/code&gt;. It does not modify the global &lt;code&gt;shadow&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inside &lt;code&gt;inner_lamp()&lt;/code&gt;, when &lt;code&gt;print(shadow)&lt;/code&gt; is called, Python searches for &lt;code&gt;shadow&lt;/code&gt; using the LEGB rule. It finds &lt;code&gt;shadow = 20&lt;/code&gt; in its &lt;em&gt;enclosing&lt;/em&gt; scope (&lt;code&gt;outer_lamp&lt;/code&gt;), and that's the &lt;code&gt;shadow&lt;/code&gt; it uses.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;lamp()&lt;/code&gt; call, which executes &lt;code&gt;inner_lamp()&lt;/code&gt;, thus prints 20. The global &lt;code&gt;shadow&lt;/code&gt; (which remains 10) is untouched, and the &lt;code&gt;shadow&lt;/code&gt; in &lt;code&gt;outer_lamp()&lt;/code&gt; casts its own shadow, independent of the global lamp.&lt;/p&gt;
&lt;h3&gt;
  
  
  Part 6: The Illusion of Locality
&lt;/h3&gt;

&lt;p&gt;Now, a subtle twist:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;global&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nf"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;What happens here?&lt;/p&gt;

&lt;p&gt;Python raises an error:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nb"&gt;UnboundLocalError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cannot&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;shadow&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="n"&gt;assignment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because Python sees the assignment &lt;code&gt;shadow = "local"&lt;/code&gt; and &lt;em&gt;assumes&lt;/em&gt; that &lt;code&gt;shadow&lt;/code&gt; must be local to &lt;code&gt;inner&lt;/code&gt;. It does not look outside anymore. So when you try to read &lt;code&gt;shadow&lt;/code&gt; before assigning it, Python is confused. There is a local &lt;code&gt;shadow&lt;/code&gt;, but it has no value yet.&lt;/p&gt;

&lt;p&gt;In Python, &lt;strong&gt;any assignment to a variable within a function makes that variable local to that function&lt;/strong&gt; , unless explicitly declared otherwise.&lt;/p&gt;

&lt;p&gt;This leads us to the next teaching.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Part 7:&lt;/strong&gt; Declaring Intent – &lt;code&gt;global&lt;/code&gt; and &lt;code&gt;nonlocal&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If we wish to use or modify a variable from an outer scope, we must &lt;em&gt;declare&lt;/em&gt; our intent.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;global&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;modify&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;shadow&lt;/span&gt;
    &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

&lt;span class="nf"&gt;modify&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, &lt;code&gt;global shadow&lt;/code&gt; tells Python: “I mean the &lt;code&gt;shadow&lt;/code&gt; from the module’s top level.”&lt;/p&gt;

&lt;p&gt;When working with nested functions, and using &lt;code&gt;nonlocal&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;outer&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;nonlocal&lt;/span&gt; &lt;span class="n"&gt;shadow&lt;/span&gt;
        &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="nf"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;outer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# 10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Without &lt;code&gt;nonlocal&lt;/code&gt;, the assignment would create a new local &lt;code&gt;shadow&lt;/code&gt; inside &lt;code&gt;inner&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
With it, Python understands: &lt;em&gt;use the variable from the enclosing function&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Extinguishing The Lamp&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The Master's second lamp illuminated the truth: each lamp casts its own shadow. Similarly, in Python, each scope can define its own variables, creating distinct 'lamps' of variables. Without explicit instruction (like &lt;code&gt;nonlocal&lt;/code&gt; or &lt;code&gt;global&lt;/code&gt;), an assignment always defaults to the current, innermost scope, safeguarding higher-level variables from unintended modification.&lt;/p&gt;

&lt;p&gt;Understanding scope is not just about avoiding errors; it's about designing clear, predictable, and maintainable code. It's about knowing where your variables truly reside, and how their light extends, or does not extend, into the surrounding code.&lt;/p&gt;

&lt;p&gt;If you enjoyed this post, consider subscribing or sharing it with your friends:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pythonkoans.substack.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21qoUC%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fpythonkoans.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D-1479521753%2526version%253D9" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pythonkoans.substack.com/" rel="noopener noreferrer" class="c-link"&gt;
            Python Koans | Vivis Dev | Substack
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Python lessons wrapped in koans. Small puzzles, deep truths. Not your usual tutorial thread. Click to read Python Koans, by Vivis Dev, a Substack publication with hundreds of subscribers.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%213kVI%21%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F7ff23720-c183-45a5-bec1-55293cc5a7f6%252Ffavicon.ico"&gt;
          pythonkoans.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Understanding how Python evaluates default arguments and why mutable defaults can carry unintended memory</title>
      <dc:creator>Vivis Dev</dc:creator>
      <pubDate>Sat, 20 Sep 2025 12:47:00 +0000</pubDate>
      <link>https://forem.com/vivis_dev/understanding-how-python-evaluates-default-arguments-and-why-mutable-defaults-can-carry-unintended-1ee6</link>
      <guid>https://forem.com/vivis_dev/understanding-how-python-evaluates-default-arguments-and-why-mutable-defaults-can-carry-unintended-1ee6</guid>
      <description>&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!pwqW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61c23f6f-4cd1-4341-aa7e-1235f58d3eeb_1394x872.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30io7sgtzpbur9yefs4q.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mutable Default Arguments and the Echoes of the Past
&lt;/h3&gt;

&lt;p&gt;In Python, a function is not just a block of code, it is an object, alive with memory. And sometimes, that memory is louder than you expect.&lt;/p&gt;

&lt;p&gt;We expect that every time we call &lt;code&gt;ring()&lt;/code&gt;, it will create a new list and append &lt;code&gt;"clang"&lt;/code&gt;. But instead, each call remembers the last. Why?&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 1: When Are Defaults Evaluated?
&lt;/h3&gt;

&lt;p&gt;In Python, &lt;strong&gt;default arguments are evaluated only once&lt;/strong&gt; — at the time the function is defined, not each time it is called.&lt;/p&gt;

&lt;p&gt;So in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Python creates a new empty list &lt;em&gt;once&lt;/em&gt; and binds it to the &lt;code&gt;bell&lt;/code&gt; parameter’s default. All future calls to &lt;code&gt;ring()&lt;/code&gt; without a &lt;code&gt;bell&lt;/code&gt; argument will use &lt;em&gt;that same list&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Thus:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;second&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;third&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# True
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;They are all the same object.&lt;/p&gt;

&lt;p&gt;This behavior often surprises beginners. It feels as if Python is remembering things it should forget. But it is not magic. It is memory: persistent, and shared.&lt;/p&gt;
&lt;h3&gt;
  
  
  Part 2: Mutable vs Immutable Defaults: A Gentle Contrast
&lt;/h3&gt;

&lt;p&gt;The issue arises only with &lt;strong&gt;mutable&lt;/strong&gt; default values, like lists or dictionaries. What if we used an &lt;strong&gt;immutable&lt;/strong&gt; default, such as a tuple?&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
   &lt;span class="n"&gt;bell&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clang&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bell&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;What do you expect?&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# ('clang',)
&lt;/span&gt;&lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# ('clang',)
&lt;/span&gt;&lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# ('clang',)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Each call gives us a fresh result. There is no accumulation. No echo.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because tuples are immutable. The &lt;code&gt;+=&lt;/code&gt; operator cannot modify the existing tuple. Instead, it creates a new one. In Python, the &lt;code&gt;id()&lt;/code&gt;function gives you the memory address of an object. You can use this to test if the objects are the same, but beware of the &lt;a href="https://pythonkoans.substack.com/i/167246035/part-the-subtle-trap-of-immutable-types" rel="noopener noreferrer"&gt;CPython caching (also known as interning) we learnt about last week&lt;/a&gt;. To avoid CPython interning, we need to assign the result to a new object:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
    &lt;span class="n"&gt;bell&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()),)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bell&lt;/span&gt;

&lt;span class="n"&gt;sound1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sound1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;# 135080430662176
&lt;/span&gt;&lt;span class="n"&gt;sound2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sound2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;# 135080430179360
&lt;/span&gt;&lt;span class="n"&gt;sound3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sound3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;# 135080430181856
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;Each time, Python builds a new object and returns it. The original default remains untouched.&lt;/p&gt;

&lt;p&gt;This is why only mutable default arguments pose a problem. It is not the default itself that is dangerous, it is the possibility of mutation.&lt;/p&gt;

&lt;p&gt;When an object can change, and you reuse that object across calls, you risk unintended persistence.&lt;/p&gt;
&lt;h3&gt;
  
  
  Part 3: The Right Way: Use &lt;code&gt;None&lt;/code&gt; as a Sentinel
&lt;/h3&gt;

&lt;p&gt;The conventional solution is this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bell&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;bell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clang&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bell&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# ['clang']
&lt;/span&gt;&lt;span class="nf"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# ['clang']
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Each call receives a fresh list.&lt;/p&gt;

&lt;p&gt;Why use &lt;code&gt;None&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;None&lt;/code&gt; is immutable and unique. It makes an excellent sentinel, a marker that tells us, “No argument was provided.”&lt;/p&gt;

&lt;p&gt;If you use this pattern, your functions will behave predictably. The echoes will fade when they should.&lt;/p&gt;


&lt;h3&gt;
  
  
  Part 4: When Might You Use Mutable Defaults?
&lt;/h3&gt;

&lt;p&gt;There &lt;em&gt;are&lt;/em&gt; rare times when shared mutable state is intended. For example, a function that memoizes its own results:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, the default dictionary acts as a persistent memory. But this is a deliberate choice, not an accident.&lt;/p&gt;

&lt;p&gt;If you do this, document it clearly. Most of the time, such patterns are better handled with decorators or external caches.&lt;/p&gt;

&lt;p&gt;Shared state is not inherently wrong, but it must be a conscious design, not an accidental side effect.&lt;/p&gt;


&lt;h3&gt;
  
  
  Best Practices: The Bellmaker’s Notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoid using mutable objects like lists or dicts as default arguments.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
They persist across calls and can lead to surprising behavior.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use&lt;/strong&gt;&lt;code&gt;None&lt;/code&gt;&lt;strong&gt;as a default when you want a fresh object each time.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Inside the function, create a new object only when needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Immutable defaults (like&lt;/strong&gt;&lt;code&gt;None&lt;/code&gt;&lt;strong&gt;,&lt;/strong&gt;&lt;code&gt;0&lt;/code&gt;&lt;strong&gt;,&lt;/strong&gt;&lt;code&gt;''&lt;/code&gt;&lt;strong&gt;, or&lt;/strong&gt;&lt;code&gt;()&lt;/code&gt;&lt;strong&gt;) are always safe.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Operations like &lt;code&gt;+=&lt;/code&gt; on them return new objects, leaving the default unchanged.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;If you&lt;/strong&gt; &lt;em&gt;&lt;strong&gt;must&lt;/strong&gt;&lt;/em&gt;&lt;strong&gt;use a mutable default, document the behavior clearly.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Treat it as shared state and ensure your design requires it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;When debugging, use&lt;/strong&gt;&lt;code&gt;id()&lt;/code&gt;&lt;strong&gt;or logging to confirm whether the same object is reused, but beware of interning which can mislead you.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If your function “remembers” things, ask yourself: “Did I mean to ring the same bell again?”&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Closing the Circle
&lt;/h3&gt;

&lt;p&gt;The novice asked, “Why does the bell grow louder each time I call for it?”&lt;/p&gt;

&lt;p&gt;The master replied,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Because you never asked for a new bell.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Python, if a default argument is mutable, it will grow, echo, and persist across calls.&lt;/p&gt;

&lt;p&gt;To write clean Python is to know which bells echo, and when to ring a fresh one.&lt;/p&gt;

&lt;p&gt;Thanks for reading Python Koans! If you enjoyed this post, consider sharing with your friends or subscribing below:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pythonkoans.substack.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21qoUC%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fpythonkoans.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D-1479521753%2526version%253D9" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pythonkoans.substack.com/" rel="noopener noreferrer" class="c-link"&gt;
            Python Koans | Vivis Dev | Substack
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Python lessons wrapped in koans. Small puzzles, deep truths. Not your usual tutorial thread. Click to read Python Koans, by Vivis Dev, a Substack publication with hundreds of subscribers.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%213kVI%21%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F7ff23720-c183-45a5-bec1-55293cc5a7f6%252Ffavicon.ico"&gt;
          pythonkoans.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Understanding the difference between identity and equality, and why it matters more than it seems.</title>
      <dc:creator>Vivis Dev</dc:creator>
      <pubDate>Sat, 20 Sep 2025 12:46:00 +0000</pubDate>
      <link>https://forem.com/vivis_dev/understanding-the-difference-between-identity-and-equality-and-why-it-matters-more-than-it-seems-2g21</link>
      <guid>https://forem.com/vivis_dev/understanding-the-difference-between-identity-and-equality-and-why-it-matters-more-than-it-seems-2g21</guid>
      <description>&lt;p&gt;&lt;a href="https://substackcdn.com/image/fetch/$s_!um0i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0de63204-a22d-4a57-886c-a10ab2e806b3_1170x1246.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjumrjof8atl8w4fj9u0.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  "is" vs. "==": The Subtle Art of Identity and Equality in Python
&lt;/h3&gt;

&lt;p&gt;When learning Python, it's common to encounter subtle distinctions that don’t make much sense at first. One of these is the difference between &lt;code&gt;is&lt;/code&gt; and &lt;code&gt;==&lt;/code&gt;. At a glance, they both seem to answer the same question: "Are these things the same?" But in truth, they answer very different questions.&lt;/p&gt;

&lt;p&gt;Let us begin at the beginning.&lt;/p&gt;

&lt;h4&gt;
  
  
  Part 1: The Two Questions
&lt;/h4&gt;

&lt;p&gt;Python allows you to ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Do these two values have the same&lt;/strong&gt; &lt;em&gt;&lt;strong&gt;contents&lt;/strong&gt;&lt;/em&gt;&lt;strong&gt;?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You use &lt;code&gt;==&lt;/code&gt; to ask this question.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Do these two values refer to the&lt;/strong&gt; &lt;em&gt;&lt;strong&gt;same object&lt;/strong&gt;&lt;/em&gt;&lt;strong&gt;in memory?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You use &lt;code&gt;is&lt;/code&gt; to ask this question.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In simple terms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;==&lt;/code&gt; asks, &lt;em&gt;"Do they look the same?"&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;is&lt;/code&gt; asks, &lt;em&gt;"Are they the same being?"&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at a few examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# True (same contents)
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# False (different objects)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;At first, this might feel strange. If they’re both &lt;code&gt;[1, 2, 3]&lt;/code&gt;, why wouldn’t they be the same?&lt;/p&gt;

&lt;p&gt;Because Python has created two separate &lt;em&gt;lists&lt;/em&gt; in memory. They hold the same values, but they are not the same object. Like two scrolls with identical text, one copied from the other. If you edit one, the other remains unchanged.&lt;/p&gt;

&lt;p&gt;This is the heart of the difference between equality and identity.&lt;/p&gt;


&lt;h4&gt;
  
  
  Part 2: Why Use &lt;code&gt;is&lt;/code&gt; At All?
&lt;/h4&gt;

&lt;p&gt;Why not always use &lt;code&gt;==&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;is&lt;/code&gt; operator is not about equality of content, it is about &lt;strong&gt;identity&lt;/strong&gt;. There are cases where identity matters deeply. One of them is checking for a sentinel object, such as &lt;code&gt;None&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_input&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No input provided.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, you're not asking &lt;em&gt;"Does the input look like None?"&lt;/em&gt; , but &lt;em&gt;"Is the input literally the None object?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This distinction ensures clarity and precision when writing code that interacts with unique markers or singleton objects.&lt;/p&gt;


&lt;h4&gt;
  
  
  Part 3: The Subtle Trap of Immutable Types
&lt;/h4&gt;

&lt;p&gt;Now let’s tread into trickier ground. The behavior of &lt;em&gt;immutable types&lt;/em&gt; , such as integers, strings, and tuples.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# True 
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# False (on most systems)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;But then:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# True 
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# True
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Why the inconsistency?&lt;/p&gt;

&lt;p&gt;This is due to &lt;a href="https://bugs.python.org/issue34850" rel="noopener noreferrer"&gt;an implementation detail of CPython&lt;/a&gt; (the most widely used Python interpreter). For performance, CPython caches small integers and some strings. So small values may end up pointing to the &lt;em&gt;same&lt;/em&gt; object. But this is not something you should rely on. It's a quirk, not a guarantee.&lt;/p&gt;

&lt;p&gt;In other words: the student sees that &lt;code&gt;1 is 1&lt;/code&gt;, and assumes the same for &lt;code&gt;1000 is 1000&lt;/code&gt;. But the truth changes subtly with scale, like mist lifting to reveal two paths instead of one.&lt;/p&gt;


&lt;h4&gt;
  
  
  Part 4: Containers and Identity
&lt;/h4&gt;

&lt;p&gt;Let’s go a step deeper. What about empty containers?&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="c1"&gt;# True - same contents (both empty)
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="c1"&gt;# False - distinct list objects
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Python creates a new list each time you write &lt;code&gt;[]&lt;/code&gt;. They are separate, though they appear the same. Like the student’s scrolls, word-for-word identical, yet changes to one do not touch the other.&lt;/p&gt;

&lt;p&gt;And tuples?&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# True 
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# True (usually)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here again, we encounter CPython’s caching. Because &lt;code&gt;()&lt;/code&gt; is immutable and common, Python may reuse the same object. But this behavior is not mandated by the Python language specification, it is an implementation detail.&lt;/p&gt;

&lt;p&gt;From Python 3.8 onwards, certain comparisons like &lt;code&gt;() is ()&lt;/code&gt; even raise a &lt;strong&gt;SyntaxWarning&lt;/strong&gt; , gently nudging the student to rethink their assumption.&lt;/p&gt;


&lt;h4&gt;
  
  
  Best Practices
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use&lt;/strong&gt;&lt;code&gt;==&lt;/code&gt; when comparing values.&lt;br&gt;&lt;br&gt;
You’re asking, &lt;em&gt;do these two things hold the same meaning or content?&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use&lt;/strong&gt;&lt;code&gt;is&lt;/code&gt; when checking for identity, especially for singletons like &lt;code&gt;None&lt;/code&gt;, &lt;code&gt;True&lt;/code&gt;, &lt;code&gt;False&lt;/code&gt;, or a sentinel object you define.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoid using&lt;/strong&gt;&lt;code&gt;is&lt;/code&gt;&lt;strong&gt;with literals or immutable values&lt;/strong&gt; , unless you have a specific reason to care about identity, and even then, document that reason.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;h4&gt;
  
  
  Final Reflection
&lt;/h4&gt;

&lt;p&gt;The master’s lesson was simple:&lt;br&gt;&lt;br&gt;
Two scrolls may appear the same. But when you change one, the truth of their separation is revealed.&lt;/p&gt;

&lt;p&gt;In Python, &lt;code&gt;==&lt;/code&gt; and &lt;code&gt;is&lt;/code&gt; teach us to look beneath the surface. Equality is about appearance. Identity is about essence. And understanding this difference is not just a technical detail, it is a practice in seeing things as they truly are.&lt;/p&gt;

&lt;p&gt;Thanks for reading Python Koans! If you enjoyed this post, consider sharing with your friends or subscribing below:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pythonkoans.substack.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21qoUC%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fpythonkoans.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D-1479521753%2526version%253D9" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pythonkoans.substack.com/" rel="noopener noreferrer" class="c-link"&gt;
            Python Koans | Vivis Dev | Substack
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Python lessons wrapped in koans. Small puzzles, deep truths. Not your usual tutorial thread. Click to read Python Koans, by Vivis Dev, a Substack publication with hundreds of subscribers.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%213kVI%21%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F7ff23720-c183-45a5-bec1-55293cc5a7f6%252Ffavicon.ico"&gt;
          pythonkoans.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>python</category>
      <category>programming</category>
      <category>koan</category>
    </item>
    <item>
      <title>Understanding truthiness, falsiness, and the quiet meaning of emptiness in Python</title>
      <dc:creator>Vivis Dev</dc:creator>
      <pubDate>Thu, 11 Sep 2025 23:46:50 +0000</pubDate>
      <link>https://forem.com/vivis_dev/understanding-truthiness-falsiness-and-the-quiet-meaning-of-emptiness-in-python-26il</link>
      <guid>https://forem.com/vivis_dev/understanding-truthiness-falsiness-and-the-quiet-meaning-of-emptiness-in-python-26il</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg6eyphmi00h55a6bj3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg6eyphmi00h55a6bj3p.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Teaching: &lt;code&gt;__bool__&lt;/code&gt; and the Design of Truth
&lt;/h2&gt;

&lt;p&gt;In Python, truth is not a binary of True and False. It is a reflection of meaning. Python asks, “In the context of logic, what does this object say about itself?”&lt;/p&gt;

&lt;p&gt;To control how an object behaves in a boolean context, such as in an if statement, you may define the &lt;code&gt;__bool__&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Let us begin again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: The Interpreter’s Question
&lt;/h2&gt;

&lt;p&gt;When Python evaluates an object in a boolean context like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;some_object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It performs this sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Call &lt;code&gt;some_object.__bool__()&lt;/code&gt;, if defined.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If not defined, fall back to &lt;code&gt;some_object.__len__()&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If length is 0, treat as False.&lt;/li&gt;
&lt;li&gt;Otherwise, treat as True.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If neither are defined, default to True&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thus, even a custom class can define its own measure of truth.&lt;/p&gt;
&lt;h2&gt;
  
  
  Part 2: Writing Our Own Truth
&lt;/h2&gt;

&lt;p&gt;Let us construct a simple object: a container of wisdom.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Scroll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, let us observe its truth:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Scroll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Do not seek the truth, only cease to cherish opinions.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The scroll speaks.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The scroll is silent.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will always print "The scroll speaks.", because Scroll has no &lt;code&gt;__bool__&lt;/code&gt; or &lt;code&gt;__len__&lt;/code&gt;. Python defaults to treating all objects as True unless told otherwise.&lt;/p&gt;

&lt;p&gt;Now, let us give it a voice of truth:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Scroll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__bool__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, observe:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nc"&gt;Scroll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Behaves like False
&lt;/span&gt;&lt;span class="nc"&gt;Scroll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Peace&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Behaves like True
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We have taught our object how to express its presence.&lt;/p&gt;
&lt;h2&gt;
  
  
  Part 3: Falling Back on &lt;code&gt;__len__&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;__bool__&lt;/code&gt; is not defined, Python looks for &lt;code&gt;__len__&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WisdomBasket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sayings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__len__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sayings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;basket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WisdomBasket&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;basket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;There is wisdom.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The basket is empty.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is useful when your object represents a collection, and truth is tied to its contents.&lt;/p&gt;
&lt;h2&gt;
  
  
  Part 4: Truth as a Design Decision
&lt;/h2&gt;

&lt;p&gt;Why does this matter?&lt;/p&gt;

&lt;p&gt;Because in real-world code, truthiness is often meaning. Consider:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;What should response mean?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did the HTTP request succeed?&lt;/li&gt;
&lt;li&gt;Did the response body contain useful data?&lt;/li&gt;
&lt;li&gt;Was the status 200?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You decide what &lt;code&gt;__bool__&lt;/code&gt; should convey.&lt;/p&gt;

&lt;p&gt;When designing APIs, internal libraries, or user-defined classes, a clear definition of truth can reduce boilerplate and make intent self-evident.&lt;/p&gt;
&lt;h2&gt;
  
  
  Part 5: A Caution on Ambiguity
&lt;/h2&gt;

&lt;p&gt;Truthiness should be unambiguous and intuitive.&lt;/p&gt;

&lt;p&gt;If your object represents a configuration, a request, or a resource, consider what it means to be “truthy.” Avoid cleverness that makes the logic obscure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# What does this mean?
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Better to document, or avoid using truthiness altogether if the semantics are unclear.&lt;/p&gt;
&lt;h2&gt;
  
  
  Closing the Circle
&lt;/h2&gt;

&lt;p&gt;The master did not say, "The empty list is False."&lt;br&gt;
He said, "Not false. Only empty."&lt;/p&gt;

&lt;p&gt;In Python, truth is not fixed. It is a shadow cast by your design. You may choose how your objects reflect light, or the absence of it.&lt;/p&gt;



&lt;p&gt;If you enjoyed this post, consider subscribing or sharing it with your friends:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pythonkoans.substack.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21qoUC%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fpythonkoans.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D-1479521753%2526version%253D9" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pythonkoans.substack.com/" rel="noopener noreferrer" class="c-link"&gt;
            Python Koans | Vivis Dev | Substack
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Python lessons wrapped in koans. Small puzzles, deep truths. Not your usual tutorial thread. Click to read Python Koans, by Vivis Dev, a Substack publication with hundreds of subscribers.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%213kVI%21%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F7ff23720-c183-45a5-bec1-55293cc5a7f6%252Ffavicon.ico"&gt;
          pythonkoans.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>python</category>
      <category>programming</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
