<?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: Samuel Ochaba</title>
    <description>The latest articles on Forem by Samuel Ochaba (@samuel_ochaba_eb9c875fa89).</description>
    <link>https://forem.com/samuel_ochaba_eb9c875fa89</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%2F3602341%2F962160fe-c301-4314-bc6b-9e98da6f00c8.png</url>
      <title>Forem: Samuel Ochaba</title>
      <link>https://forem.com/samuel_ochaba_eb9c875fa89</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/samuel_ochaba_eb9c875fa89"/>
    <language>en</language>
    <item>
      <title>Refactoring is not a bag of tricks. It is a professional discipline.</title>
      <dc:creator>Samuel Ochaba</dc:creator>
      <pubDate>Thu, 09 Apr 2026 12:17:44 +0000</pubDate>
      <link>https://forem.com/samuel_ochaba_eb9c875fa89/refactoring-is-not-a-bag-of-tricks-it-is-a-professional-discipline-5agp</link>
      <guid>https://forem.com/samuel_ochaba_eb9c875fa89/refactoring-is-not-a-bag-of-tricks-it-is-a-professional-discipline-5agp</guid>
      <description>&lt;p&gt;We built &lt;strong&gt;TekBreed&lt;/strong&gt; because we think many developers are not struggling from a lack of information.&lt;/p&gt;

&lt;p&gt;They are struggling from a lack of the right kind of training.&lt;/p&gt;

&lt;p&gt;There is a difference.&lt;/p&gt;

&lt;p&gt;Information tells you what something is.&lt;br&gt;&lt;br&gt;
Training helps you operate when the situation is unclear, messy, or high-stakes.&lt;/p&gt;

&lt;p&gt;That difference is especially visible in refactoring.&lt;/p&gt;

&lt;p&gt;That is why one of the sprints we are most excited about on TekBreed is:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Refactoring Mastery: Thinking &amp;amp; Building Like a Senior Engineer&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Right now, during early access, the &lt;strong&gt;first milestone of that sprint is open for free until April 13, 2026&lt;/strong&gt;, along with the first milestone of every premium sprint on the platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why refactoring was one of the first topics we wanted to teach this way
&lt;/h2&gt;

&lt;p&gt;Refactoring looks simple when you only see the polished version.&lt;/p&gt;

&lt;p&gt;A blog post shows ugly code.&lt;br&gt;&lt;br&gt;
Then it shows cleaner code.&lt;br&gt;&lt;br&gt;
Then it explains the technique.&lt;/p&gt;

&lt;p&gt;That is useful, but it does not fully train the real skill.&lt;/p&gt;

&lt;p&gt;Behind the scenes, the real challenge in refactoring is not just knowing a technique.&lt;br&gt;&lt;br&gt;
It is knowing how to evaluate structure under real constraints.&lt;/p&gt;

&lt;p&gt;That includes questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is safe to change right now?&lt;/li&gt;
&lt;li&gt;What behavior must be preserved?&lt;/li&gt;
&lt;li&gt;What part of this code is actually causing pain?&lt;/li&gt;
&lt;li&gt;Should this be refactored now, later, or not at all?&lt;/li&gt;
&lt;li&gt;Is this change improving the system or just making it look nicer?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is closer to real engineering work.&lt;/p&gt;

&lt;p&gt;And that is the kind of thinking we want TekBreed to support.&lt;/p&gt;

&lt;h2&gt;
  
  
  What TekBreed is trying to do differently
&lt;/h2&gt;

&lt;p&gt;TekBreed is a software engineering learning platform built around deeper understanding, practical thinking, and more realistic skill development.&lt;/p&gt;

&lt;p&gt;We are not trying to be just another place to consume tutorials.&lt;/p&gt;

&lt;p&gt;We want the platform to help learners:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build stronger mental models&lt;/li&gt;
&lt;li&gt;Move beyond surface-level code familiarity&lt;/li&gt;
&lt;li&gt;Interact with ideas more actively&lt;/li&gt;
&lt;li&gt;Get help when they are stuck&lt;/li&gt;
&lt;li&gt;Think more like practicing engineers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is also where &lt;strong&gt;TekChat&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why TekChat matters
&lt;/h2&gt;

&lt;p&gt;A learner usually does not need help only before or after a lesson.&lt;/p&gt;

&lt;p&gt;They need help in the middle of it.&lt;/p&gt;

&lt;p&gt;Inside the moment where something almost makes sense, but not quite.&lt;br&gt;&lt;br&gt;
Inside the moment where a concept becomes slippery.&lt;br&gt;&lt;br&gt;
Inside the moment where they need one more explanation, one more example, or one more reframing.&lt;/p&gt;

&lt;p&gt;That is where &lt;strong&gt;TekChat&lt;/strong&gt; is meant to be useful.&lt;/p&gt;

&lt;p&gt;Behind the scenes, the goal is not to remove effort.&lt;br&gt;&lt;br&gt;
The goal is to support effort.&lt;/p&gt;

&lt;p&gt;We want TekChat to help learners keep moving when they hit friction, instead of quietly dropping off.&lt;/p&gt;

&lt;p&gt;And because we are in early access, we want people to use TekChat however they want and tell us where it is genuinely helpful and where it still needs work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the refactoring sprint is a strong place to start
&lt;/h2&gt;

&lt;p&gt;The refactoring sprint represents a lot of what we care about.&lt;/p&gt;

&lt;p&gt;It is not only about code cleanup.&lt;br&gt;&lt;br&gt;
It is about engineering judgment.&lt;/p&gt;

&lt;p&gt;It is about learning to think carefully about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structure&lt;/li&gt;
&lt;li&gt;Safety&lt;/li&gt;
&lt;li&gt;Behavior preservation&lt;/li&gt;
&lt;li&gt;Tradeoffs&lt;/li&gt;
&lt;li&gt;Decision-making under constraints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why the sprint is called:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Refactoring Mastery: Thinking &amp;amp; Building Like a Senior Engineer&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We wanted the framing to be honest.&lt;/p&gt;

&lt;p&gt;This is not “10 refactoring tricks.”&lt;br&gt;&lt;br&gt;
It is a more serious attempt to teach what better engineering judgment looks like in practice.&lt;/p&gt;

&lt;p&gt;And because the &lt;strong&gt;first milestone is open&lt;/strong&gt;, people can experience the direction of that sprint right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  What early access looks like
&lt;/h2&gt;

&lt;p&gt;From now until &lt;strong&gt;April 13, 2026&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TekBreed is open for early access&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;first milestone of every premium sprint&lt;/strong&gt; is free&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TekChat&lt;/strong&gt; can be used freely&lt;/li&gt;
&lt;li&gt;Feedback is strongly encouraged&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, from &lt;strong&gt;April 13 through April 30, 2026&lt;/strong&gt;, subscribers get &lt;strong&gt;30% off&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is not meant to be a soft, polite preview.&lt;/p&gt;

&lt;p&gt;We want real usage.&lt;/p&gt;

&lt;p&gt;We want people to test the product properly.&lt;br&gt;&lt;br&gt;
We want them to try the refactoring sprint.&lt;br&gt;&lt;br&gt;
We want them to use TekChat heavily.&lt;br&gt;&lt;br&gt;
We want them to tell us what feels sharp, what feels unclear, and what breaks.&lt;/p&gt;

&lt;p&gt;Because that is how the platform gets better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the first milestone
&lt;/h2&gt;

&lt;p&gt;If you are interested in refactoring, engineering judgment, or learning environments that go beyond passive tutorials, this is a good time to try TekBreed.&lt;/p&gt;

&lt;p&gt;Start with the first milestone of the refactoring sprint.&lt;br&gt;&lt;br&gt;
Use TekChat while you go through it.&lt;br&gt;&lt;br&gt;
Then leave feedback.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tekbreed.com/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=soft_launch_2026" rel="noopener noreferrer"&gt;TekBreed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tekbreed.com/sprints/refactoring-mastery-thinking-and-building-like-a-senior-engineer?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=soft_launch_2026" rel="noopener noreferrer"&gt;Refactoring sprint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tekbreed.com/about?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=soft_launch_2026" rel="noopener noreferrer"&gt;About TekBreed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tekbreed.com/sprints/refactoring-mastery-thinking-and-building-like-a-senior-engineer?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=soft_launch_2026" rel="noopener noreferrer"&gt;Early access announcement&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>refactoring</category>
      <category>softwareengineering</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>The Python Bug That Bites Every Developer (Exactly Once)</title>
      <dc:creator>Samuel Ochaba</dc:creator>
      <pubDate>Fri, 23 Jan 2026 13:59:05 +0000</pubDate>
      <link>https://forem.com/samuel_ochaba_eb9c875fa89/the-python-bug-that-bites-every-developer-exactly-once-4m8j</link>
      <guid>https://forem.com/samuel_ochaba_eb9c875fa89/the-python-bug-that-bites-every-developer-exactly-once-4m8j</guid>
      <description>&lt;p&gt;I stared at my screen for 20 minutes, convinced Python was broken.&lt;/p&gt;

&lt;p&gt;My function was supposed to return a fresh list every time. Instead, it was accumulating items like it had a memory. The third call somehow contained results from the first two.&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;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt;
    &lt;span class="n"&gt;items&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="n"&gt;item&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;items&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;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apple&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;   &lt;span class="c1"&gt;# ['apple'] ✓
&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;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;banana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# ['apple', 'banana'] ???
&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;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cherry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# ['apple', 'banana', 'cherry'] !!!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I expected &lt;code&gt;['banana']&lt;/code&gt;. I got &lt;code&gt;['apple', 'banana']&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is Python's &lt;strong&gt;mutable default argument bug&lt;/strong&gt; — and every Python developer gets bitten by it exactly once.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mental Model That's Lying to You
&lt;/h2&gt;

&lt;p&gt;When you see &lt;code&gt;items=[]&lt;/code&gt;, your brain reads: &lt;em&gt;"If no list is provided, create an empty one."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That's wrong.&lt;/p&gt;

&lt;p&gt;What Python actually does: &lt;em&gt;"Create one empty list **right now, when the function is defined&lt;/em&gt;&lt;em&gt;, and reuse that same object for every call."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The default value isn't &lt;em&gt;"an empty list"&lt;/em&gt; — it's a &lt;strong&gt;reference to one specific list object&lt;/strong&gt; that was created when Python first read your function.&lt;/p&gt;

&lt;p&gt;Let me prove it:&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;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;=&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;List id: &lt;/span&gt;&lt;span class="si"&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;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;items&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="n"&gt;item&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;items&lt;/span&gt;

&lt;span class="nf"&gt;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# List id: 4399504832
&lt;/span&gt;&lt;span class="nf"&gt;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# List id: 4399504832  ← Same object!
&lt;/span&gt;&lt;span class="nf"&gt;add_item&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="c1"&gt;# List id: 4399504832  ← Still same!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every call uses the &lt;strong&gt;exact same list object&lt;/strong&gt;. When you append to it, that change persists.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix: Use None as a Sentinel
&lt;/h2&gt;

&lt;p&gt;The solution is elegant once you understand the problem:&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;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&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;items&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;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;  &lt;span class="c1"&gt;# Create a NEW list each call
&lt;/span&gt;    &lt;span class="n"&gt;items&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="n"&gt;item&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;items&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;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apple&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;   &lt;span class="c1"&gt;# ['apple']
&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;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;banana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# ['banana'] ✓
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now each call that doesn't provide &lt;code&gt;items&lt;/code&gt; gets a &lt;strong&gt;fresh list&lt;/strong&gt;, created at call time, not definition time.&lt;/p&gt;

&lt;p&gt;This pattern should become automatic for any mutable default:&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;# Lists
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&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;items&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;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# Dictionaries
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&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;config&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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# Sets
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seen&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;seen&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;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&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;h2&gt;
  
  
  Why This Actually Matters
&lt;/h2&gt;

&lt;p&gt;This isn't just a gotcha for interviews. It causes real bugs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Caching gone wrong&lt;/strong&gt;: Your memoization function "remembers" too much&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API handlers accumulating state&lt;/strong&gt;: Each request sees data from previous requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test pollution&lt;/strong&gt;: Tests pass individually but fail when run together&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've seen this bug in production code from senior engineers. It's subtle because &lt;strong&gt;the function works perfectly the first time&lt;/strong&gt; — the bug only appears on subsequent calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Deeper Lesson
&lt;/h2&gt;

&lt;p&gt;Python's object model is consistent: variables are names pointing to objects. When you write &lt;code&gt;items=[]&lt;/code&gt;, you're creating an object and storing a reference to it. That reference is evaluated &lt;strong&gt;once&lt;/strong&gt; — when the &lt;code&gt;def&lt;/code&gt; statement runs.&lt;/p&gt;

&lt;p&gt;Understanding this doesn't just help you avoid one bug. It unlocks how Python actually works under the hood.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article is adapted from my upcoming book, **Zero to AI Engineer: Python Foundations&lt;/em&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I share excerpts like this on &lt;a href="https://substack.com/@samuelochaba" rel="noopener noreferrer"&gt;Substack&lt;/a&gt; — follow along for more!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>beginners</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Why You Freeze When Starting a New Project (And 5 Questions to Fix It)</title>
      <dc:creator>Samuel Ochaba</dc:creator>
      <pubDate>Sun, 18 Jan 2026 15:32:00 +0000</pubDate>
      <link>https://forem.com/samuel_ochaba_eb9c875fa89/why-you-freeze-when-starting-a-new-project-and-5-questions-to-fix-it-57mm</link>
      <guid>https://forem.com/samuel_ochaba_eb9c875fa89/why-you-freeze-when-starting-a-new-project-and-5-questions-to-fix-it-57mm</guid>
      <description>&lt;p&gt;&lt;strong&gt;You know the feeling.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You have a great idea for a project. You open your code editor. You create &lt;code&gt;main.py&lt;/code&gt;. The blinking cursor waits.&lt;/p&gt;

&lt;p&gt;And you freeze.&lt;/p&gt;

&lt;p&gt;What do you type first? Do you need a class? A function? A variable? Ten minutes later, you're checking Reddit because the paralyzed feeling is too uncomfortable.&lt;/p&gt;

&lt;p&gt;This happens not because you can't code, but because you're trying to do two things at once: &lt;strong&gt;designing&lt;/strong&gt; the software and &lt;strong&gt;building&lt;/strong&gt; it.&lt;/p&gt;

&lt;p&gt;Professional developers don't start by writing code. They start by asking questions.&lt;/p&gt;

&lt;p&gt;Here is the &lt;strong&gt;Five Questions Framework&lt;/strong&gt; I teach my students to turn any vague idea into a concrete blueprint before writing a single line of Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. WHAT does it do? (The Menu)
&lt;/h2&gt;

&lt;p&gt;Imagine you're opening a restaurant. Before you buy stoves or hire chefs, you need a menu.&lt;/p&gt;

&lt;p&gt;Your "menu" is your feature list. Don't worry about &lt;em&gt;how&lt;/em&gt; to build it yet. Just list &lt;em&gt;what&lt;/em&gt; it does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: A Simple Contact Book&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add new contacts&lt;/li&gt;
&lt;li&gt;View all contacts&lt;/li&gt;
&lt;li&gt;Search by name&lt;/li&gt;
&lt;li&gt;Save to file&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. WHO does the work? (The Staff)
&lt;/h2&gt;

&lt;p&gt;In a restaurant, the host greets you, the chef cooks, and the dishwasher cleans. They have distinct &lt;strong&gt;responsibilities&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Group your features into "staff members" or layers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User Interface (UI):&lt;/strong&gt; The "Host". Displays menus, asks for input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business Logic:&lt;/strong&gt; The "Chef". Adds, searches, and deletes contacts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage:&lt;/strong&gt; The "Archive Manager". Saves and loads JSON files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. HOW are they connected? (The Kitchen Flow)
&lt;/h2&gt;

&lt;p&gt;The host doesn't cook the burger. The host hands an order to the chef.&lt;/p&gt;

&lt;p&gt;Draw a simple dependency line:&lt;br&gt;
&lt;code&gt;UI -&amp;gt; Logic -&amp;gt; Storage&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This tells you something critical: &lt;strong&gt;The Storage layer doesn't know the UI exists.&lt;/strong&gt; This is "Separation of Concerns," and it prevents your code from becoming spaghetti.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. WHAT flows between them? (The Dish)
&lt;/h2&gt;

&lt;p&gt;When the host hands an order to the chef, what is written on the ticket?&lt;/p&gt;

&lt;p&gt;Decide your data structure now.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is a "Contact" a tuple? &lt;code&gt;("Alice", "555-1234")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Or a dictionary? &lt;code&gt;{"name": "Alice", "phone": "555-1234"}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choosing a &lt;strong&gt;list of dictionaries&lt;/strong&gt; now prevents painful refactors later.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. WHAT operations exist? (The Recipes)
&lt;/h2&gt;

&lt;p&gt;Now, listing the functions becomes easy. You just map the work to the "staff":&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storage Layer:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;load_contacts()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;save_contacts()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Logic Layer:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;add_contact()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;search_contacts()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;UI Layer:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;display_menu()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_user_input()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The "Aha" Moment
&lt;/h2&gt;

&lt;p&gt;By answering these five questions, you didn't write code, but you just designed a full architecture.&lt;/p&gt;

&lt;p&gt;When you finally return to &lt;code&gt;main.py&lt;/code&gt;, the paralysis is gone. You aren't "building a contact book." You're just implementing &lt;code&gt;load_contacts()&lt;/code&gt;. Then &lt;code&gt;display_menu()&lt;/code&gt;. Small, manageable tasks.&lt;/p&gt;

&lt;p&gt;Stop staring at the cursor. Start asking questions.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This framework is adapted from my upcoming book, &lt;strong&gt;Zero to AI Engineer: Python Foundations&lt;/strong&gt;, where we use it to design everything from CLI tools to AI pipelines.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I share excerpts like this on Substack → &lt;a href="https://substack.com/@samuelochaba" rel="noopener noreferrer"&gt;https://substack.com/@samuelochaba&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>beginners</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Why Your Python Code Takes Hours Instead of Seconds (A 3-Line Fix)</title>
      <dc:creator>Samuel Ochaba</dc:creator>
      <pubDate>Mon, 12 Jan 2026 12:48:44 +0000</pubDate>
      <link>https://forem.com/samuel_ochaba_eb9c875fa89/why-your-python-code-takes-hours-instead-of-seconds-a-3-line-fix-i0l</link>
      <guid>https://forem.com/samuel_ochaba_eb9c875fa89/why-your-python-code-takes-hours-instead-of-seconds-a-3-line-fix-i0l</guid>
      <description>&lt;p&gt;Your code works. It passes every test. Then you run it on real data and go make coffee. Then lunch. Then you start questioning your career choices.&lt;/p&gt;

&lt;p&gt;Here's the pattern that's probably killing your performance:&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;has_duplicates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&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;items&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&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="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&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;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This checks for duplicates by comparing every pair. Perfectly logical. Perfectly correct. Perfectly &lt;strong&gt;devastating&lt;/strong&gt; at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Math That Ruins Your Day
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;List Size&lt;/th&gt;
&lt;th&gt;Comparisons&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;4,950&lt;/td&gt;
&lt;td&gt;Instant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;499,500&lt;/td&gt;
&lt;td&gt;Instant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;49,995,000&lt;/td&gt;
&lt;td&gt;Noticeable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;td&gt;~5 billion&lt;/td&gt;
&lt;td&gt;Many seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;~500 billion&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Hours&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That nested loop means comparisons grow with the &lt;em&gt;square&lt;/em&gt; of your data. Double the input, quadruple the time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3-Line Fix
&lt;/h2&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;has_duplicates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&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;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This version is essentially &lt;strong&gt;instant&lt;/strong&gt; even for a million items.&lt;/p&gt;

&lt;p&gt;Why? Converting to a set is O(n) — you touch each element once. Checking length is O(1). No nested loops, no quadratic explosion.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern to Recognize
&lt;/h2&gt;

&lt;p&gt;Whenever you see nested loops over the same collection, alarm bells should ring:&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;# 🚨 O(n²) — danger zone
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;collection&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;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;other_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# This is a hidden loop!
&lt;/span&gt;        &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# ✅ O(n) — convert first
&lt;/span&gt;&lt;span class="n"&gt;other_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;collection&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;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;other_set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# O(1) lookup
&lt;/span&gt;        &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That innocent &lt;code&gt;if item in other_list&lt;/code&gt; is doing a linear scan each time. With a set, it's a hash lookup — constant time regardless of size.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real-World Impact
&lt;/h2&gt;

&lt;p&gt;This isn't academic. In AI engineering, you're constantly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deduplicating embeddings&lt;/li&gt;
&lt;li&gt;Checking if items exist in large collections&lt;/li&gt;
&lt;li&gt;Processing batches where nested loops sneak in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference between O(n²) and O(n) is the difference between "works on my machine" and "works in production."&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is adapted from my upcoming book, Zero to AI Engineer: Python Foundations.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I share excerpts like this on Substack → &lt;a href="https://substack.com/@samuelochaba" rel="noopener noreferrer"&gt;https://substack.com/@samuelochaba&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>performance</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Python Sets: remove() vs discard() — When Silence Is Golden</title>
      <dc:creator>Samuel Ochaba</dc:creator>
      <pubDate>Sun, 11 Jan 2026 13:16:22 +0000</pubDate>
      <link>https://forem.com/samuel_ochaba_eb9c875fa89/python-sets-remove-vs-discard-when-silence-is-golden-1j1k</link>
      <guid>https://forem.com/samuel_ochaba_eb9c875fa89/python-sets-remove-vs-discard-when-silence-is-golden-1j1k</guid>
      <description>&lt;p&gt;Python sets have two methods for removing elements: &lt;code&gt;remove()&lt;/code&gt; and &lt;code&gt;discard()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;They look identical:&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;colors&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;red&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;green&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;blue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;green&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# Works
&lt;/span&gt;&lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;discard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;green&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# Also works
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But watch what happens when the element doesn't exist:&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;colors&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;red&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;blue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yellow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# KeyError: 'yellow'
&lt;/span&gt;&lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;discard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yellow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Nothing happens. No error.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;remove()&lt;/code&gt; raises an exception. &lt;code&gt;discard()&lt;/code&gt; fails silently.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Each
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;remove()&lt;/code&gt; when missing elements are bugs:&lt;/strong&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;deactivate_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active_users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;active_users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Should exist!
&lt;/span&gt;    &lt;span class="c1"&gt;# If it doesn't, something's wrong upstream
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;user_id&lt;/code&gt; isn't in &lt;code&gt;active_users&lt;/code&gt;, that's a logic error in your code. You &lt;em&gt;want&lt;/em&gt; the exception to surface it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;discard()&lt;/code&gt; when missing elements are expected:&lt;/strong&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;cleanup_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;discard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# May already be gone
&lt;/span&gt;    &lt;span class="c1"&gt;# User might have logged out twice, that's fine
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the session might have already been cleaned up. No need to crash over it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI Engineering Context
&lt;/h2&gt;

&lt;p&gt;This pattern appears constantly in data pipelines:&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;# Processing documents for RAG
&lt;/span&gt;&lt;span class="n"&gt;processed_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;document_stream&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;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;processed_ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;

    &lt;span class="n"&gt;processed_ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Cleanup: remove from pending queue
&lt;/span&gt;    &lt;span class="n"&gt;pending_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;discard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Might not be there
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;discard()&lt;/code&gt; here prevents crashes when the same document appears in multiple streams.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decision Framework
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Because&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Element &lt;em&gt;should&lt;/em&gt; exist&lt;/td&gt;
&lt;td&gt;&lt;code&gt;remove()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Missing = bug, surface it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Element &lt;em&gt;might&lt;/em&gt; exist&lt;/td&gt;
&lt;td&gt;&lt;code&gt;discard()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Missing = expected, ignore it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idempotent operations&lt;/td&gt;
&lt;td&gt;&lt;code&gt;discard()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Safe to call multiple times&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strict state management&lt;/td&gt;
&lt;td&gt;&lt;code&gt;remove()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Catch invalid transitions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  One More Option
&lt;/h2&gt;

&lt;p&gt;If you want to know &lt;em&gt;whether&lt;/em&gt; removal happened without raising an error, combine &lt;code&gt;in&lt;/code&gt; with &lt;code&gt;discard()&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;element&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;my_set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;my_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;discard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&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;Removed&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;Wasn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t there&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;Though this is rarely necessary—usually you either care (use &lt;code&gt;remove()&lt;/code&gt;) or you don't (use &lt;code&gt;discard()&lt;/code&gt; and move on).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which do you default to?&lt;/strong&gt; Do you prefer loud failures or silent forgiveness?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is adapted from my upcoming book, Zero to AI Engineer: Python Foundations.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;I share excerpts like this on Substack → &lt;a href="https://substack.com/@samuelochaba" rel="noopener noreferrer"&gt;https://substack.com/@samuelochaba&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Python Dictionary Views Are Live (And It Might Break Your Code)</title>
      <dc:creator>Samuel Ochaba</dc:creator>
      <pubDate>Sat, 10 Jan 2026 19:16:04 +0000</pubDate>
      <link>https://forem.com/samuel_ochaba_eb9c875fa89/python-dictionary-views-are-live-and-it-might-break-your-code-56in</link>
      <guid>https://forem.com/samuel_ochaba_eb9c875fa89/python-dictionary-views-are-live-and-it-might-break-your-code-56in</guid>
      <description>&lt;p&gt;Quick quiz. What does this print?&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&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;b&lt;/span&gt;&lt;span class="sh"&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="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&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;keys&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="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="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&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;keys&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 said:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dict_keys(['a', 'b'])
dict_keys(['a', 'b'])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're wrong.&lt;/p&gt;

&lt;p&gt;It prints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dict_keys(['a', 'b'])
dict_keys(['a', 'b', 'c'])  # ← Updated automatically!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Dictionary Views Are Live
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;.keys()&lt;/code&gt;, &lt;code&gt;.values()&lt;/code&gt;, and &lt;code&gt;.items()&lt;/code&gt; don't return copies.&lt;/p&gt;

&lt;p&gt;They return &lt;strong&gt;view objects&lt;/strong&gt;—live windows into the dictionary that update automatically when the dictionary changes.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;person&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;name&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;Alice&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;age&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;
&lt;span class="nf"&gt;dict_keys&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&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;age&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NYC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Add a key
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;                       &lt;span class="c1"&gt;# View updated automatically
&lt;/span&gt;&lt;span class="nf"&gt;dict_keys&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&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;age&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;city&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 by design. Views are memory-efficient—they don't copy data.&lt;/p&gt;

&lt;p&gt;But it can cause bugs if you're not expecting it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bug This Causes
&lt;/h2&gt;

&lt;p&gt;Imagine you're iterating over dictionary keys while modifying the dictionary:&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;config&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;debug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;verbose&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temp_flag&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Remove all flags that are True
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&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;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# 💥 RuntimeError!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RuntimeError: dictionary changed size during iteration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The view is live. When you delete a key, the view changes mid-iteration. Python catches this and throws an error.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;If you need to modify while iterating, convert to a list first:&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;config&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;debug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;verbose&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temp_flag&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Convert to list - creates a snapshot
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&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;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&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;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# {'verbose': False}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;list(config.keys())&lt;/code&gt; creates a &lt;strong&gt;static copy&lt;/strong&gt; at that moment. Now you can modify the original dictionary freely.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Live Views Are Useful
&lt;/h2&gt;

&lt;p&gt;Live views aren't just a gotcha—they're actually powerful:&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;# Monitor changes in real-time
&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="n"&gt;cache_keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# ... elsewhere in your code ...
&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_123&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="n"&gt;data&lt;/span&gt;
&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_456&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="n"&gt;more_data&lt;/span&gt;

&lt;span class="c1"&gt;# cache_keys automatically reflects all additions
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&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;Cached: &lt;/span&gt;&lt;span class="si"&gt;{&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;cache_keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Always current
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No need to re-fetch &lt;code&gt;.keys()&lt;/code&gt; every time. The view stays synchronized.&lt;/p&gt;

&lt;p&gt;Did you know dictionary views were live? Has this ever caught you off guard?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is adapted from my upcoming book, Zero to AI Engineer: Python Foundations.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I share excerpts like this on Substack → &lt;a href="https://substack.com/@samuelochaba" rel="noopener noreferrer"&gt;https://substack.com/@samuelochaba&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Why Your Python Tuple Can't Be a Dictionary Key</title>
      <dc:creator>Samuel Ochaba</dc:creator>
      <pubDate>Fri, 09 Jan 2026 09:02:43 +0000</pubDate>
      <link>https://forem.com/samuel_ochaba_eb9c875fa89/why-your-python-tuple-cant-be-a-dictionary-key-5307</link>
      <guid>https://forem.com/samuel_ochaba_eb9c875fa89/why-your-python-tuple-cant-be-a-dictionary-key-5307</guid>
      <description>&lt;p&gt;"Tuples are immutable" is one of the first things you learn about Python tuples.&lt;/p&gt;

&lt;p&gt;But check this out:&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&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="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;# Tuple of lists
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&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="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# Modify the list inside
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&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;99&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;# It changed!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait—didn't we say tuples can't be modified?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tuple itself didn't change.&lt;/strong&gt; It still contains exactly two references to the same two list objects. What changed is the &lt;em&gt;content&lt;/em&gt; of one of those list objects.&lt;/p&gt;

&lt;p&gt;Think of it like a display case with two fish tanks inside. You can't add or remove tanks from the case (the tuple is immutable), but the fish inside each tank can still swim around (the lists are mutable).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This has real consequences:&lt;/strong&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&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="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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Traceback &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="nb"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;unhashable&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tuples containing mutable objects &lt;strong&gt;cannot be hashed&lt;/strong&gt;—which means they can't be dictionary keys or set members.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters for AI engineering:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you're working with embeddings, model outputs, or cached results, you might think "I'll use a tuple as a cache key." But if that tuple contains any mutable objects (lists, dicts, sets), you'll get a &lt;code&gt;TypeError&lt;/code&gt; at runtime.&lt;/p&gt;

&lt;p&gt;The fix: ensure all elements are also immutable:&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;# This works as a cache key
&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# This fails
&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# if input_ids is a list
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;This is adapted from my upcoming book, Zero to AI Engineer: Python Foundations.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;I share excerpts like this on Substack → &lt;a href="https://substack.com/@samuelochaba" rel="noopener noreferrer"&gt;https://substack.com/@samuelochaba&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>computerscience</category>
      <category>ai</category>
    </item>
    <item>
      <title>You Know Python Basics—Now Let's Build Something Real</title>
      <dc:creator>Samuel Ochaba</dc:creator>
      <pubDate>Thu, 08 Jan 2026 13:56:26 +0000</pubDate>
      <link>https://forem.com/samuel_ochaba_eb9c875fa89/you-know-python-basics-now-lets-build-something-real-1pco</link>
      <guid>https://forem.com/samuel_ochaba_eb9c875fa89/you-know-python-basics-now-lets-build-something-real-1pco</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Who this is for:&lt;/strong&gt; You've completed a Python basics course or tutorial. You understand variables, loops, conditionals, and strings. But you haven't built anything real yet. This project is specifically for that stage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You've learned variables, loops, conditionals, and string methods. Each concept made sense in isolation. But when you stared at a blank file and tried to build something... nothing came together.&lt;/p&gt;

&lt;p&gt;This is the gap between knowing syntax and &lt;em&gt;actually programming&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I just open-sourced a project specifically designed to bridge that gap: a text adventure game that combines all those foundational concepts into one playable program.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/samuel-ochaba-dev/zero-to-ai-engineer-projects/tree/main/dungeon-escape-text-adventure-game" rel="noopener noreferrer"&gt;github.com/samuel-ochaba-dev/zero-to-ai-engineer-projects&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What You'll Practice
&lt;/h2&gt;

&lt;p&gt;This isn't just another tutorial—it's a consolidation project. Every Python basic you've learned has a job:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;How It's Used&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Variables &amp;amp; Data Types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Game state, rooms, player info&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dictionaries&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nested data for the game world&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Operators&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Health checks, item membership&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;String Methods&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.strip()&lt;/code&gt;, &lt;code&gt;.lower()&lt;/code&gt;, &lt;code&gt;.split()&lt;/code&gt;, &lt;code&gt;.join()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;User Input&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Interactive &lt;code&gt;input()&lt;/code&gt; game loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Conditionals&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;if-elif-else&lt;/code&gt; and &lt;code&gt;match/case&lt;/code&gt; for commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;While Loops&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Main game loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;For Loops&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Iterating inventory with &lt;code&gt;enumerate()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Type Hints&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Self-documenting function signatures&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The point isn't to learn new syntax. The point is to &lt;em&gt;combine&lt;/em&gt; concepts you already know.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Get the Most from This
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Don't just run the code.&lt;/strong&gt; That's the mistake most people make with learning projects.&lt;/p&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Read the code without running it first
&lt;/h3&gt;

&lt;p&gt;Trace through the main loop. Predict what happens when you type &lt;code&gt;go north&lt;/code&gt; or &lt;code&gt;take torch&lt;/code&gt;. &lt;em&gt;Then&lt;/em&gt; run it to check your mental model.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Break something intentionally
&lt;/h3&gt;

&lt;p&gt;Remove a line. Change a condition. See what error you get. This teaches you what each piece is actually doing.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Extend it
&lt;/h3&gt;

&lt;p&gt;The repo includes practice exercises:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a new room with items&lt;/li&gt;
&lt;li&gt;Implement a locked door puzzle (requires a key)&lt;/li&gt;
&lt;li&gt;Add a scoring system&lt;/li&gt;
&lt;li&gt;Create new random events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building on existing code is how real projects work.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Pattern You'll Keep Using
&lt;/h2&gt;

&lt;p&gt;The main game loop looks like 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;while&lt;/span&gt; &lt;span class="n"&gt;game_running&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Check win/lose conditions
&lt;/span&gt;    &lt;span class="c1"&gt;# Display current state
&lt;/span&gt;    &lt;span class="c1"&gt;# Get player input
&lt;/span&gt;    &lt;span class="c1"&gt;# Process command
&lt;/span&gt;    &lt;span class="c1"&gt;# Trigger random events
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern—a loop that reads input, processes it, updates state, and displays results—appears &lt;em&gt;everywhere&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CLI tools&lt;/li&gt;
&lt;li&gt;Chat applications&lt;/li&gt;
&lt;li&gt;AI chatbots (read user message → send to LLM → display response → repeat)&lt;/li&gt;
&lt;li&gt;Game engines&lt;/li&gt;
&lt;li&gt;REPLs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you understand this pattern deeply, you'll recognize it in every interactive program you encounter.&lt;/p&gt;




&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python 3.10+&lt;/strong&gt; (required for &lt;code&gt;match/case&lt;/code&gt; syntax)&lt;/li&gt;
&lt;li&gt;No external dependencies
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/samuel-ochaba-dev/zero-to-ai-engineer-projects.git
&lt;span class="nb"&gt;cd &lt;/span&gt;zero-to-ai-engineer-projects/dungeon-escape-text-adventure-game
python3 adventure_game.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Sample Gameplay
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;========================================
       DUNGEON ESCAPE
========================================

You wake up in a dark dungeon.
Find the exit to escape!
Type 'help' for available commands.

You are in the Entrance Hall.
A dusty entrance with cobwebs covering the walls.
A faint light flickers from the north.

Exits: north, east
Items here: torch

Health: 100 | Inventory: empty

What do you do? &amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate through rooms, collect items, survive random events, and find the exit.&lt;/p&gt;




&lt;p&gt;The mental shift from "I know what a while loop is" to "I can use a while loop to build a game loop" is significant. This project is designed to make that shift concrete.&lt;/p&gt;

&lt;p&gt;Clone it. Break it. Extend it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is adapted from my upcoming book, Zero to AI Engineer: Python Foundations.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;I share excerpts like this on Substack → &lt;a href="https://substack.com/@samuelochaba" rel="noopener noreferrer"&gt;https://substack.com/@samuelochaba&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>beginners</category>
      <category>gamedev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Type Hints Make AI Code Generation Significantly Better</title>
      <dc:creator>Samuel Ochaba</dc:creator>
      <pubDate>Wed, 07 Jan 2026 07:54:47 +0000</pubDate>
      <link>https://forem.com/samuel_ochaba_eb9c875fa89/type-hints-make-ai-code-generation-significantly-better-2286</link>
      <guid>https://forem.com/samuel_ochaba_eb9c875fa89/type-hints-make-ai-code-generation-significantly-better-2286</guid>
      <description>&lt;p&gt;If you're using AI coding assistants without type hints, you're leaving performance on the table.&lt;/p&gt;

&lt;p&gt;When you ask an AI to complete 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;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# TODO: split by comma and return uppercase words
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI has to guess what &lt;code&gt;data&lt;/code&gt; is. A string? A file? A list?&lt;/p&gt;

&lt;p&gt;But with type hints:&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;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="c1"&gt;# TODO: split by comma and return uppercase words
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the AI knows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;data&lt;/code&gt; is definitely a string&lt;/li&gt;
&lt;li&gt;It should return a list of strings&lt;/li&gt;
&lt;li&gt;Methods like &lt;code&gt;.split()&lt;/code&gt; and &lt;code&gt;.upper()&lt;/code&gt; are appropriate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The result:&lt;/strong&gt; More accurate completions, fewer hallucinations, less back-and-forth.&lt;/p&gt;

&lt;p&gt;This extends to entire codebases. When your functions have type hints, AI tools can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate code that matches your existing types&lt;/li&gt;
&lt;li&gt;Suggest appropriate methods for the given types&lt;/li&gt;
&lt;li&gt;Catch inconsistencies in their own output&lt;/li&gt;
&lt;li&gt;Understand relationships between modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where this matters most:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API handlers (FastAPI uses type hints for automatic validation)&lt;/li&gt;
&lt;li&gt;Data processing pipelines&lt;/li&gt;
&lt;li&gt;Any code that interfaces with AI-generated components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Type hints are documentation for both humans &lt;em&gt;and&lt;/em&gt; machines. In 2026, that dual purpose matters more than ever.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is adapted from my upcoming book, Zero to AI Engineer: Python Foundations.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I share excerpts like this on Substack → &lt;a href="https://substack.com/@samuelochaba" rel="noopener noreferrer"&gt;https://substack.com/@samuelochaba&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>typing</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Python Comprehensions Are Declarative (And Why That Matters)</title>
      <dc:creator>Samuel Ochaba</dc:creator>
      <pubDate>Tue, 06 Jan 2026 00:48:33 +0000</pubDate>
      <link>https://forem.com/samuel_ochaba_eb9c875fa89/python-comprehensions-are-declarative-and-why-that-matters-17ba</link>
      <guid>https://forem.com/samuel_ochaba_eb9c875fa89/python-comprehensions-are-declarative-and-why-that-matters-17ba</guid>
      <description>&lt;p&gt;Two ways to square every number in a list:&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;# Version A
&lt;/span&gt;&lt;span class="n"&gt;squares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;squares&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="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Version B
&lt;/span&gt;&lt;span class="n"&gt;squares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Version A tells Python &lt;strong&gt;how&lt;/strong&gt; to build the list:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create empty list&lt;/li&gt;
&lt;li&gt;Loop through numbers&lt;/li&gt;
&lt;li&gt;Compute square&lt;/li&gt;
&lt;li&gt;Append to list&lt;/li&gt;
&lt;li&gt;Repeat&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Version B tells Python &lt;strong&gt;what&lt;/strong&gt; you want:&lt;br&gt;
"A list of squared numbers."&lt;/p&gt;

&lt;p&gt;This is the difference between &lt;strong&gt;imperative&lt;/strong&gt; and &lt;strong&gt;declarative&lt;/strong&gt; code.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Declarative Wins
&lt;/h2&gt;

&lt;p&gt;Consider reading code you didn't write. With Version A, you have to trace the loop logic to understand the result. With Version B, the intent is immediate.&lt;/p&gt;

&lt;p&gt;More importantly, declarative code is &lt;strong&gt;harder to mess up&lt;/strong&gt;. In Version A, you could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Forget to initialize &lt;code&gt;squares = []&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Misspell &lt;code&gt;.append()&lt;/code&gt; as &lt;code&gt;.appnd()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Accidentally append the wrong value&lt;/li&gt;
&lt;li&gt;Break the loop prematurely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Version B eliminates these failure modes. The pattern is baked in.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Newspaper Test
&lt;/h2&gt;

&lt;p&gt;Ask yourself: "Can I understand this comprehension in one quick read?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Passes:&lt;/strong&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="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;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fails:&lt;/strong&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="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="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="k"&gt;if&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="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;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&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;2&lt;/span&gt; &lt;span class="o"&gt;+&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;2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;50&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 have to stop and puzzle it out, the comprehension is too complex. Use a loop instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Loops Are Better
&lt;/h2&gt;

&lt;p&gt;Comprehensions aren't always right. Use loops when:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multiple statements per item:&lt;/strong&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;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;results&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="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Side effects:&lt;/strong&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="c1"&gt;# DON'T do this - creates useless list of Nones
&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;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# DO this
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&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;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Complex control flow:&lt;/strong&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;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;validate&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;continue&lt;/span&gt;
    &lt;span class="k"&gt;if&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;score&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="nf"&gt;process&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Mindset Shift
&lt;/h2&gt;

&lt;p&gt;Think of comprehensions as &lt;strong&gt;expressions that produce collections&lt;/strong&gt;, not loops that build them.&lt;/p&gt;

&lt;p&gt;When you write &lt;code&gt;[f(x) for x in items]&lt;/code&gt;, you're saying: "This IS a list of f(x) values." Not "This becomes a list through these steps."&lt;/p&gt;

&lt;p&gt;Once you internalize this, you'll reach for comprehensions naturally—and know when a loop serves you better.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is adapted from my upcoming book, Zero to AI Engineer: Python Foundations.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;I share excerpts like this on Substack → &lt;a href="https://substack.com/@samuelochaba" rel="noopener noreferrer"&gt;https://substack.com/@samuelochaba&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>cleancode</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Python Matrix Gotcha That Silently Corrupts Your Data</title>
      <dc:creator>Samuel Ochaba</dc:creator>
      <pubDate>Tue, 06 Jan 2026 00:47:19 +0000</pubDate>
      <link>https://forem.com/samuel_ochaba_eb9c875fa89/the-python-matrix-gotcha-that-silently-corrupts-your-data-1h7b</link>
      <guid>https://forem.com/samuel_ochaba_eb9c875fa89/the-python-matrix-gotcha-that-silently-corrupts-your-data-1h7b</guid>
      <description>&lt;p&gt;Here's innocent-looking code to create a 2×3 matrix of zeros:&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;row&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;0&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;matrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;matrix&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;0&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="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;0&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks perfect. Now let's change one cell:&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;matrix&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;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;matrix&lt;/span&gt;
&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;99&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;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;99&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;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;    &lt;span class="c1"&gt;# BOTH rows changed!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We modified &lt;code&gt;matrix[0][0]&lt;/code&gt;, but &lt;code&gt;matrix[1][0]&lt;/code&gt; changed too. What happened?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Aliasing
&lt;/h2&gt;

&lt;p&gt;When we wrote &lt;code&gt;[row for _ in range(2)]&lt;/code&gt;, we didn't create two rows. We created two &lt;strong&gt;references to the same row&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;matrix[0] ──┐
            ├──► [99, 0, 0]  (same list object)
matrix[1] ──┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every "row" in our matrix is the same object in memory. Change one, change all.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix: Create New Lists
&lt;/h2&gt;

&lt;p&gt;Each row must be a &lt;strong&gt;new&lt;/strong&gt; list 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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;matrix&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;0&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;matrix&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;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;matrix&lt;/span&gt;
&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;99&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;0&lt;/span&gt;&lt;span class="p"&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;0&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="c1"&gt;# Only first row changed ✓
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or more generally:&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cols&lt;/span&gt; &lt;span class="o"&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;matrix&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The inner comprehension &lt;code&gt;[0 for _ in range(cols)]&lt;/code&gt; runs fresh for each row, creating a new list each time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Trips People Up
&lt;/h2&gt;

&lt;p&gt;The original code &lt;em&gt;reads&lt;/em&gt; correctly: "make a list of &lt;code&gt;row&lt;/code&gt;, two times."&lt;/p&gt;

&lt;p&gt;But in Python, &lt;code&gt;row&lt;/code&gt; is a reference. Repeating a reference doesn't copy the underlying object—it just creates more pointers to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Diagnostic
&lt;/h2&gt;

&lt;p&gt;Suspicious your matrix has this bug? Check with &lt;code&gt;is&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bad_matrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bad_matrix&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="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;bad_matrix&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="bp"&gt;True&lt;/span&gt;    &lt;span class="c1"&gt;# Same object! Bug confirmed.
&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;good_matrix&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;0&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;good_matrix&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="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;good_matrix&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="bp"&gt;False&lt;/span&gt;   &lt;span class="c1"&gt;# Different objects. Safe.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Rule
&lt;/h2&gt;

&lt;p&gt;When creating nested structures with comprehensions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never repeat a reference. Always create fresh.&lt;/strong&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="c1"&gt;# WRONG - reuses same list
&lt;/span&gt;&lt;span class="n"&gt;template&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;0&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="n"&gt;matrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="c1"&gt;# RIGHT - creates new list each time
&lt;/span&gt;&lt;span class="n"&gt;matrix&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;0&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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;This applies to lists of lists, lists of dicts, lists of any mutable object.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is adapted from my upcoming book, Zero to AI Engineer: Python Foundations.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;I share excerpts like this on Substack → &lt;a href="https://substack.com/@samuelochaba" rel="noopener noreferrer"&gt;https://substack.com/@samuelochaba&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>bugs</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Why sum(x**2 for x in range(1000000)) Uses 4000x Less Memory</title>
      <dc:creator>Samuel Ochaba</dc:creator>
      <pubDate>Tue, 06 Jan 2026 00:45:58 +0000</pubDate>
      <link>https://forem.com/samuel_ochaba_eb9c875fa89/why-sumx2-for-x-in-range1000000-uses-4000x-less-memory-4a9p</link>
      <guid>https://forem.com/samuel_ochaba_eb9c875fa89/why-sumx2-for-x-in-range1000000-uses-4000x-less-memory-4a9p</guid>
      <description>&lt;p&gt;Pop quiz. Which uses less memory?&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;# Option A
&lt;/span&gt;&lt;span class="nf"&gt;sum&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;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;

&lt;span class="c1"&gt;# Option B
&lt;/span&gt;&lt;span class="nf"&gt;sum&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;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only difference is the brackets. But Option B uses &lt;strong&gt;~4000x less memory&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Measure It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;nums&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;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list_comp&lt;/span&gt; &lt;span class="o"&gt;=&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;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;gen_exp&lt;/span&gt; &lt;span class="o"&gt;=&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;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_comp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;824456&lt;/span&gt;    &lt;span class="c1"&gt;# ~800 KB
&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gen_exp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;200&lt;/span&gt;       &lt;span class="c1"&gt;# ~200 bytes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The list comprehension creates 100,000 integers in memory &lt;strong&gt;immediately&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The generator expression? It's just a recipe. It computes values &lt;strong&gt;on demand&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Generator Expressions Work
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;squares_list&lt;/span&gt; &lt;span class="o"&gt;=&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;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;  &lt;span class="c1"&gt;# 1M items NOW
&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;squares_gen&lt;/span&gt; &lt;span class="o"&gt;=&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;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;   &lt;span class="c1"&gt;# Nothing yet
&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;squares_gen&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;genexpr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generator object is tiny because it doesn't &lt;em&gt;store&lt;/em&gt; values—it &lt;em&gt;produces&lt;/em&gt; them when asked.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Catch: Single Use
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# Try again
&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;               &lt;span class="c1"&gt;# Empty! Generator exhausted.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generators can only be iterated &lt;strong&gt;once&lt;/strong&gt;. After that, they're spent.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Each
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Generator Expression&lt;/th&gt;
&lt;th&gt;List Comprehension&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Large data, single pass&lt;/td&gt;
&lt;td&gt;Need random access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feeding into &lt;code&gt;sum()&lt;/code&gt;, &lt;code&gt;max()&lt;/code&gt;, etc.&lt;/td&gt;
&lt;td&gt;Need to iterate multiple times&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory is a concern&lt;/td&gt;
&lt;td&gt;Need &lt;code&gt;len()&lt;/code&gt; or indexing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Pro Tip: Skip the Extra Parentheses
&lt;/h2&gt;

&lt;p&gt;When a generator is the &lt;strong&gt;only argument&lt;/strong&gt; to a function, you don't need double parentheses:&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;# These are equivalent
&lt;/span&gt;&lt;span class="nf"&gt;sum&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;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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;sum&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;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="c1"&gt;# Cleaner
&lt;/span&gt;
&lt;span class="c1"&gt;# Same with other functions
&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&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;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;any&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;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;all&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;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;Those brackets aren't just syntax—they're a &lt;strong&gt;memory decision&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;[...]&lt;/code&gt; = "Build everything now"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(...)&lt;/code&gt; = "I'll figure it out as I go"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you're processing large data and only need one pass, drop the brackets. Your RAM will thank you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is adapted from my upcoming book, Zero to AI Engineer: Python Foundations.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;I share excerpts like this on Substack → &lt;a href="https://substack.com/@samuelochaba" rel="noopener noreferrer"&gt;https://substack.com/@samuelochaba&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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