Create an account


Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tut] Python Small Integer Caching: == versus is

#1
Python Small Integer Caching: == versus is

<div><p>This interesting code snippet was brought to my attention by <a href="https://blog.finxter.com/subscribe/">Finxter reader</a> Albrecht.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">a, b = 250, 250
for i in range(250, 260): if a is not b: break a += 1 b += 1
print(a)
# What's the output of this code snippet?</pre>
<p>You’d guess that the for loop goes from <code>i=250</code> to <code>i=259</code>, each time incrementing <code>a</code> and <code>b</code>. As Python creates one integer object to which both names refer, the command <code>a is not b</code> should always be <code>False</code>. Thus, the result is <code>a=259</code>, right?</p>
<p><strong>WRONG!!! $%&amp;&amp;%$</strong></p>
<p>Try it yourself in our interactive code shell:</p>
<p> <iframe src="https://trinket.io/embed/python/c5b84ba145" width="100%" height="356" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe> </p>
<p><em><strong>Exercise</strong>: Run the code and check the result. Did you expect this?</em></p>
<p><strong><em>The result is <code>a=257</code>.</em></strong></p>
<p>The reason is an implementation detail of the <a href="https://github.com/python/cpython/blob/4830f581af57dd305c02c1fd72299ecb5b090eca/Objects/longobject.c#L18-L23">CPython implementation</a> called <a href="https://docs.python.org/3/c-api/long.html">“Small Integer Caching”</a> — the internal cache of integers in Python. </p>
<figure class="wp-block-image size-large"><img src="https://i0.wp.com/blog.finxter.com/wp-content/uploads/2019/12/objects-scaled.jpg?fit=1024%2C576&amp;ssl=1" alt="" class="wp-image-5451" srcset="https://blog.finxter.com/wp-content/uploads/2019/12/objects-scaled.jpg 1024w, https://blog.finxter.com/wp-content/uplo...00x169.jpg 300w, https://blog.finxter.com/wp-content/uplo...68x432.jpg 768w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>
<p>If you create an integer object that falls into the range of -5 to 256, Python will only return a reference to this object — which is already cached in memory. </p>
<blockquote class="wp-block-quote">
<p><em>“The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you actually just get back a reference to the existing object.”</em> </p>
<p><cite>Python <a href="https://docs.python.org/3/c-api/long.html">Docs</a></cite></p></blockquote>
<p>You can visualize the code execution in this interactive memory visualizer:</p>
<p> <iframe width="800" height="500" frameborder="0" src="https://pythontutor.com/iframe-embed.html#code=a,%20b%20%3D%20250,%20250%0Afor%20i%20in%20range%28250,%20260%29%3A%0A%20%20%20%20if%20a%20is%20not%20b%3A%0A%20%20%20%20%20%20%20%20break%0A%20%20%20%20a%20%2B%3D%201%0A%20%20%20%20b%20%2B%3D%201%0Aprint%28a%29%0A%23%20What's%20the%20output%20of%20this%20code%20snippet%3F&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=33&heapPrimitives=nevernest&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe> </p>
<p><em><strong>Exercise</strong>: Click next until you see the result. How many integers are in memory?</em></p>
<p>Let’s quickly examine the meaning of “is” in Python.</p>
<h2>The is operator</h2>
<p>The <a href="https://docs.python.org/3.6/reference/expressions.html#is">is operator</a> checks if two variable names point to the same object in memory:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">>>> a = "hello"
>>> b = "hello"
>>> a is b
True</pre>
<p>Both variables <code>a</code> and <code>b</code> point to the string <code>"hello"</code>. Python doesn’t store the same string twice but creates it only once in memory. This saves memory and makes Python faster and more efficient. And it’s not a problem because strings are immutable — so one variable cannot “overshadow” a string object of another variable. </p>
<p>Note that we can use the <code>id()</code> function to check an integer representation of the memory address:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">>>> a = "hello"
>>> b = "hello"
>>> id(a)
1505840752992
>>> id(b)
1505840752992</pre>
<p>They both point to the same location in memory! Therefore, the <code>is</code> operator returns <code>True</code>!</p>
<h2>Small Integer Caching</h2>
<p>Again, if you create an integer object that falls into the range of -5 to 256, Python will only return a reference to this object — which is already cached in memory. But if we create an integer object that does not fall into this range, Python may return a new integer object with the same value.</p>
<figure class="wp-block-image size-large"><img src="https://i1.wp.com/blog.finxter.com/wp-content/uploads/2019/12/objects2-scaled.jpg?fit=1024%2C576&amp;ssl=1" alt="" class="wp-image-5452" srcset="https://blog.finxter.com/wp-content/uploads/2019/12/objects2-scaled.jpg 1024w, https://blog.finxter.com/wp-content/uplo...00x169.jpg 300w, https://blog.finxter.com/wp-content/uplo...68x432.jpg 768w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>
<p>If we now check <code>a is not b</code>, Python will give us the correct result <code>True</code>. </p>
<p>In fact, this leads to the strange behavior of the C implementation of Python 3:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a is b
False</pre>
<p>Therefore, you should always compare integers by using the <code>==</code> operator in Python. This ensures that Python performs a semantic comparison, and not a mere memory address comparison:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">>>> a = 256
>>> b = 256
>>> a == b
True
>>> a = 257
>>> b = 257
>>> a == b
True</pre>
<p><strong>What can you learn from this? Implementation details matter!</strong></p>
<h2>Where to Go From Here?</h2>
<p>Enough theory, let’s get some practice!</p>
<p>To become successful in coding, you need to get out there and solve real problems for real people. That’s how you can become a six-figure earner easily. And that’s how you polish the skills you really need in practice. After all, what’s the use of learning theory that nobody ever needs?</p>
<p><strong>Practice projects is how you sharpen your saw in coding!</strong></p>
<p>Do you want to become a code master by focusing on practical code projects that actually earn you money and solve problems for people?</p>
<p>Then become a Python freelance developer! It’s the best way of approaching the task of improving your Python skills—even if you are a complete beginner.</p>
<p>Join my free webinar <a rel="noreferrer noopener" href="https://blog.finxter.com/webinar-freelancer/" target="_blank">“How to Build Your High-Income Skill Python”</a> and watch how I grew my coding business online and how you can, too—from the comfort of your own home.</p>
<p><a href="https://blog.finxter.com/webinar-freelancer/" target="_blank" rel="noreferrer noopener">Join the free webinar now!</a></p>
</div>


https://www.sickgaming.net/blog/2020/08/...versus-is/
Reply



Forum Jump:


Users browsing this thread:
2 Guest(s)

Forum software by © MyBB Theme © iAndrew 2016