{"id":117100,"date":"2020-08-22T21:04:00","date_gmt":"2020-08-22T21:04:00","guid":{"rendered":"https:\/\/blog.finxter.com\/?p=5450"},"modified":"2020-08-22T21:04:00","modified_gmt":"2020-08-22T21:04:00","slug":"python-small-integer-caching-versus-is","status":"publish","type":"post","link":"https:\/\/sickgaming.net\/blog\/2020\/08\/22\/python-small-integer-caching-versus-is\/","title":{"rendered":"Python Small Integer Caching: == versus is"},"content":{"rendered":"<p>This interesting code snippet was brought to my attention by <a href=\"https:\/\/blog.finxter.com\/subscribe\/\">Finxter reader<\/a> Albrecht.<\/p>\n<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\nfor i in range(250, 260): if a is not b: break a += 1 b += 1\nprint(a)\n# What's the output of this code snippet?<\/pre>\n<p>You&#8217;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>\n<p><strong>WRONG!!! $%&amp;&amp;%$<\/strong><\/p>\n<p>Try it yourself in our interactive code shell:<\/p>\n<p> <iframe loading=\"lazy\" src=\"https:\/\/trinket.io\/embed\/python\/c5b84ba145\" width=\"100%\" height=\"356\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" allowfullscreen><\/iframe> <\/p>\n<p><em><strong>Exercise<\/strong>: Run the code and check the result. Did you expect this?<\/em><\/p>\n<p><strong><em>The result is <code>a=257<\/code>.<\/em><\/strong><\/p>\n<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\">&#8220;Small Integer Caching&#8221;<\/a> &#8212; the internal cache of integers in Python. <\/p>\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" 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\/uploads\/2019\/12\/objects-300x169.jpg 300w, https:\/\/blog.finxter.com\/wp-content\/uploads\/2019\/12\/objects-768x432.jpg 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<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 &#8212; which is already cached in memory. <\/p>\n<blockquote class=\"wp-block-quote\">\n<p><em>\u201cThe 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.\u201d<\/em> <\/p>\n<p><cite>Python <a href=\"https:\/\/docs.python.org\/3\/c-api\/long.html\">Docs<\/a><\/cite><\/p><\/blockquote>\n<p>You can visualize the code execution in this interactive memory visualizer:<\/p>\n<p> <iframe loading=\"lazy\" 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&#038;codeDivHeight=400&#038;codeDivWidth=350&#038;cumulative=false&#038;curInstr=33&#038;heapPrimitives=nevernest&#038;origin=opt-frontend.js&#038;py=3&#038;rawInputLstJSON=%5B%5D&#038;textReferences=false\"> <\/iframe> <\/p>\n<p><em><strong>Exercise<\/strong>: Click next until you see the result. How many integers are in memory?<\/em><\/p>\n<p>Let&#8217;s quickly examine the meaning of &#8220;is&#8221; in Python.<\/p>\n<h2>The is operator<\/h2>\n<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>\n<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\"\n>>> b = \"hello\"\n>>> a is b\nTrue<\/pre>\n<p>Both variables <code>a<\/code> and <code>b<\/code> point to the string <code>\"hello\"<\/code>. Python doesn&#8217;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&#8217;s not a problem because strings are immutable &#8212; so one variable cannot &#8220;overshadow&#8221; a string object of another variable. <\/p>\n<p>Note that we can use the <code>id()<\/code> function to check an integer representation of the memory address:<\/p>\n<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\"\n>>> b = \"hello\"\n>>> id(a)\n1505840752992\n>>> id(b)\n1505840752992<\/pre>\n<p>They both point to the same location in memory! Therefore, the <code>is<\/code> operator returns <code>True<\/code>!<\/p>\n<h2>Small Integer Caching<\/h2>\n<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 &#8212; 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>\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" 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\/uploads\/2019\/12\/objects2-300x169.jpg 300w, https:\/\/blog.finxter.com\/wp-content\/uploads\/2019\/12\/objects2-768x432.jpg 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<p>If we now check <code>a is not b<\/code>, Python will give us the correct result <code>True<\/code>. <\/p>\n<p>In fact, this leads to the strange behavior of the C implementation of Python 3:<\/p>\n<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\n>>> b = 256\n>>> a is b\nTrue\n>>> a = 257\n>>> b = 257\n>>> a is b\nFalse<\/pre>\n<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>\n<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\n>>> b = 256\n>>> a == b\nTrue\n>>> a = 257\n>>> b = 257\n>>> a == b\nTrue<\/pre>\n<p><strong>What can you learn from this? Implementation details matter!<\/strong><\/p>\n<h2>Where to Go From Here?<\/h2>\n<p>Enough theory, let\u2019s get some practice!<\/p>\n<p>To become successful in coding, you need to get out there and solve real problems for real people. That\u2019s how you can become a six-figure earner easily. And that\u2019s how you polish the skills you really need in practice. After all, what\u2019s the use of learning theory that nobody ever needs?<\/p>\n<p><strong>Practice projects is how you sharpen your saw in coding!<\/strong><\/p>\n<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>\n<p>Then become a Python freelance developer! It\u2019s the best way of approaching the task of improving your Python skills\u2014even if you are a complete beginner.<\/p>\n<p>Join my free webinar <a rel=\"noreferrer noopener\" href=\"https:\/\/blog.finxter.com\/webinar-freelancer\/\" target=\"_blank\">\u201cHow to Build Your High-Income Skill Python\u201d<\/a> and watch how I grew my coding business online and how you can, too\u2014from the comfort of your own home.<\/p>\n<p><a href=\"https:\/\/blog.finxter.com\/webinar-freelancer\/\" target=\"_blank\" rel=\"noreferrer noopener\">Join the free webinar now!<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This interesting code snippet was brought to my attention by Finxter reader Albrecht. a, b = 250, 250 for i in range(250, 260): if a is not b: break a += 1 b += 1 print(a) # What&#8217;s the output of this code snippet? You&#8217;d guess that the for loop goes from i=250 to i=259, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[857],"tags":[73,468,528],"class_list":["post-117100","post","type-post","status-publish","format-standard","hentry","category-python-tut","tag-programming","tag-python","tag-tutorial"],"_links":{"self":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts\/117100","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/comments?post=117100"}],"version-history":[{"count":0,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts\/117100\/revisions"}],"wp:attachment":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/media?parent=117100"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/categories?post=117100"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/tags?post=117100"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}