Skip to content
DevMeme
5125 of 7590
Languages Post #5610 · source on Telegram

Python's Identity Crisis: The Peculiar Case of Integer Caching

Description

The image presents a programming quiz-style meme titled 'What is wrong?'. It shows two blocks of Python code with their respective outputs. The first block assigns the integer 256 to variables 'a' and 'b', and the expression 'a is b' evaluates to 'True'. The second block assigns 257 to 'a' and 'b', but this time 'a is b' evaluates to 'False'. Both blocks contain the watermark '#clcoding.com'. The technical context behind this is a CPython optimization where a range of small integers (from -5 to 256) are pre-allocated and cached. The 'is' operator checks for object identity (whether two variables point to the same memory location), not just value equality. For 256, both 'a' and 'b' point to the same cached object, so 'a is b' is True. For 257, which is outside this range, Python creates two separate objects in memory, making 'a is b' False. This behavior is a classic 'gotcha' that contradicts Python's reputation for being straightforward, as noted by the post's caption

Comments

113
Anonymous ★ Top Pick The Zen of Python says 'There should be one-- and preferably only one --obvious way to do it.' CPython's integer cache then whispers, '...unless the number is bigger than 256.'
  1. Anonymous ★ Top Pick

    The Zen of Python says 'There should be one-- and preferably only one --obvious way to do it.' CPython's integer cache then whispers, '...unless the number is bigger than 256.'

  2. Anonymous

    256 gets the VIP singleton pass in CPython’s small-int pool; 257 shows up and your ‘is’ test fails harder than your promised five-nines SLA

  3. Anonymous

    After 15 years of Python, you still explain to juniors that 'is' checks if two variables are the same person at a party, while '==' checks if they're wearing the same outfit. Then CPython's integer caching from -5 to 256 shows up like that one friend who insists on being a singleton at every gathering

  4. Anonymous

    Ah yes, the classic 'is' vs '==' interview trap that separates those who've read CPython source from those who just write Python. Nothing says 'senior engineer' quite like knowing that -5 to 256 live in a special VIP lounge while 257 has to wait outside like a peasant. It's Python's way of teaching us that identity is just a social construct... until you hit production and wonder why your cache key comparison works in dev but fails with real data. Pro tip: if your code's correctness depends on whether integers are the same object in memory, you're not writing Python - you're writing a very expensive philosophy dissertation on the nature of sameness

  5. Anonymous

    If your dedup logic uses 'a is b', congratulations - your correctness now depends on CPython’s small-int cache; your SLA is fine until a customer orders item 257

  6. Anonymous

    Python's small int cache: the singleton pattern 256 lives for, but 257 has to rent its own object - talk about class discrimination

  7. Anonymous

    Relying on CPython’s small‑int cache for correctness is how ‘is’ becomes a portability bug - works on your laptop, detonates on PyPy

  8. @rostopiradv 2y

    wtf?

  9. @affirvega 2y

    maybe 256 is byte and integer size of a byte is stored as value?

    1. @qwertymoll 2y

      No, byte max value is 255

      1. @affirvega 2y

        then i have no clue

    2. @RiedleroD 2y

      numbers from some negative number to 256 are singletons, for performance reasons. This is literally UB too, since this is only an implementation peculiarity in CPython.

  10. @Darkangeel_hd 2y

    i think it has to do with the id of values

  11. @waifu_anton 2y

    Caching, I guess. Numbers up to 256 are cached and returned as a single data type. 257 is not cached, so it is two data types in memory. At least that's how it works in Java

    1. @trainzman 2y

      yes

      1. @Darkangeel_hd 2y

        can you gives us the whole story pls ?

        1. @trainzman 2y

          https://www.codementor.io/@arpitbhayani/python-caches-integers-16jih595jk I guess this one should help

          1. @Darkangeel_hd 2y

            thanks

    2. @symptom9 2y

      This is true

  12. @Ranchonyx 2y

    This is on the same level as JavaScript fuckery

    1. @deerspangle 2y

      Not even slightly, JS is another level

      1. @Ranchonyx 2y

        I'd say this one is more obscure than JS magicks. All the crazy stuff happening in JS is well documented at least.

        1. @Araalith 2y

          JavaScript's pretty simple, until you mix numbers, objects and arrays with ToString(). Who even thought that was a good idea?

          1. @purplesyringa 2y

            and valueOf

          2. @sylfn 2y

            who even thought using JavaScript is a good idea?

          3. @sylfn 2y

            i -= -1...)

            1. @Araalith 2y

              What? -=-1 is equal to +=1

              1. @sylfn 2y

                what about types

                1. @Araalith 2y

                  There is TS to fix it.

                2. @Araalith 2y

                  Also: char * s = "12"; s-=-1; printf("%s", s);

                  1. @affirvega 2y

                    2??

    2. @RiedleroD 2y

      > checks for reference equality rather than value equality in a built-in datatype that isn't meant to be used like an object > wonders why UB is happening no, this isn't js-like fuckery

      1. @purplesyringa 2y

        This is not UB though

  13. @Darkangeel_hd 2y

    from 0 to i guess 256 each number has it's own id

  14. @trainzman 2y

    also not from 0

  15. @trainzman 2y

    but from -5

    1. @Darkangeel_hd 2y

      thanks for the correction

      1. @trainzman 2y

        it also sounds so funny

  16. dev_meme 2y

    is is used in comparisons to check whether the variables refer to the same address in memory. As the numbers up to 256 are cached in memory, it produces this counterintuitive result.

  17. @trainzman 2y

    "We cache -4"

  18. @cmdrosmium 2y

    the only reason you want to use is operator is when you want to check variable for nullability (if x is None:) I dunno is there any other scenario when you really need to test variables by their memory references

    1. @trainzman 2y

      type(X) is Y

      1. @cmdrosmium 2y

        if isinstance(X, Y): is better in most cases, because it works for subclasses too

        1. @Assarbad 2y

          And multiple types at once (second argument can be tuple of types).

    2. @gizlu 2y

      I also used x is False to avoid coercion of 0 and "" to False

  19. @anarchist47 2y

    https://ideone.com/EgC18t

    1. @RiedleroD 2y

      maybe that's not CPython. It is UB after all

  20. @Ranchonyx 2y

    My opinion may be biased due to me working with that thing on a daily basis and having gotten used to the black magic.

    1. @id8seq 2y

      Just don't use "is", You will almost never face this behavior in the code, in contrary to JS weirds

      1. @Ranchonyx 2y

        What exactly does "is" do then? Apparently it's more than comparison by value?

        1. @id8seq 2y

          It's comparison of address in memory. 'id(a) == id(b)' Though objects may be equal, but if in memory they are different you get false. If you want to compare objects you use "=="

          1. @Ranchonyx 2y

            Thanks for letting me know

  21. @id8seq 2y

    Similar thing happens for other objects as well, for example for the string. Take the quiz: https://github.com/zshimanchik/python_core_tutorial/blob/master/02_int_caching.ipynb

  22. @a125x_a 2y

    bro: literally doesn't understand what "is" does also bro: wtf i don't understand what "is" does 🤯 omg so unintuitive

  23. @pe_che 2y

    Um?

    1. @Assarbad 2y

      Wouldn't be surprised if this were both configuration and implementation specific. Essentially the "meme" is a nice example for why you should know operators and types and such and pick the right ones ... (I guess in all languages)

    2. @colllapse 2y

      print python version. reproduced on 3.11.2

      1. @RiedleroD 2y

        I checked, it depends on how you call it. https://t.me/devs_chat/98827

        1. @RiedleroD 2y

          i.e. in a file a is b → True and inside the interactive shell, a is b → False

          1. @colllapse 2y

            if repl than yes, otherwise it's done bugless i assume, yes

            1. @RiedleroD 2y

              wdym bugless, all of this is intended behaviour

            2. @endisn16h 2y

              *me sitting here for two straight minutes, trying to figure out wheres REPL part came from*

              1. @colllapse 2y

                you have three options - 1. write to a file and exec it, 2. run REPL and put them there or 3. pass some code as argument to the interpreter

                1. @endisn16h 2y

                  ik, just had a lil brain melt

  24. @beton_kruglosu_totchno 2y

    "caching numbers" makes me vomit

  25. @pe_che 2y

    Ok, I see

  26. @purplesyringa 2y

    Honestly, you're pretty damn lucky if you've only encountered this symptom in Python, not due to pointer provenance

  27. Yuri 2y

    "is" checking if it is the same object... Up to 256 ints are just a pointer, so it is literally points at the same memory cell...

    1. @RiedleroD 2y

      all ints are objects in python, but they're only singletons up until 256 (plus some negative numbers)

  28. @chupasaurus 2y

    Like writing to a log file with rotation without losing the output?

  29. @chupasaurus 2y

    Mine is too a bit. Since JS itself wasn't meant to be working with filesystems, NodeJS and other runtimes followed that by not supporting syscalls which track FS nodes.

  30. @RiedleroD 2y

    don't compare numbers with is >:I

  31. @karumsenjoyer 2y

    maybe because usually such code is written only by some software engineering babies or fools

    1. @ZgGPuo8dZef58K6hxxGVj3Z2 2y

      Wait till you realize that JavaScript uses 64 bit integers for everything, pointers, consts (like null, true, false, NaN, etc) and that objects are sometimes stored like vectors, sometimes as a fucking "butterfly" which is custom type that is a 32 bit pointer stored in a 64 bit integer and points in the middle of the array. The left side of the array stores values/pointers to the properties, and the right side stores array items if the object is also an array (which it is sometimes)

      1. @endisn16h 2y

        stop it you are scaring me

        1. @ZgGPuo8dZef58K6hxxGVj3Z2 2y

          That was not all

          1. @RiedleroD 2y

            oki imma leave now :)

  32. @ZgGPuo8dZef58K6hxxGVj3Z2 2y

    The problem is that you mixed tabs with spaces

  33. @ZgGPuo8dZef58K6hxxGVj3Z2 2y

    floating point numbers range from 0001 0000 0000 0000x to FFFE FFFF FFFF FFFFx

    1. @RiedleroD 2y

      what

    2. @purplesyringa 2y

      Wait wait isn't every bit sequence a valid fp number?

      1. @ColonelPhantom 2y

        I'm not sure about the trick described above, but there are many different NaN values which can be abused to store extra data for NaN

    3. @sylfn 2y

      where is it from

  34. @Nefrace 2y

    hmm

    1. @RiedleroD 2y

      still UB, it's not the job of python to make sure you don't do cursed shit with it

      1. @Araalith 2y

        Python exists because it babysits programmers. It's slow, hungry, and lacks power. And now you claim that it fails even in its sole purpose?

        1. @RiedleroD 2y

          python exists to be quick to code, not to babysit programmers.

          1. @Araalith 2y

            Do you think Python is quick? To me, it feels like it took the worst of both worlds: the clumsiness of compiled languages and the sluggishness of interpreted ones. Just compare it to Perl, for example, and you'll see how to achieve the same results with half the code. But sure, if by "quick" you mean "pull it from a library and pray," then you're correct... however from this perspective Bash even more "quick".

            1. @RiedleroD 2y

              it's not clumsy at all though bash is cursed

              1. @pixelsex 2y

                bash is the OG python

              2. @RiedleroD 2y

                and no, I barely ever use external libs for my python scripts

            2. @purplesyringa 2y

              Bash isn't as universal though

    2. @RiedleroD 2y

      in this case, it's because the parser somtimes likes to make two equivalent literals the same object, to save on instanciating overhead

      1. @RiedleroD 2y

        so it actually compiles to bytecode that looks something like _hidden = 257 a = _hidden b = _hidden

  35. @RiedleroD 2y

    256 is still part of the numeric range where python uses singletons

  36. @kuklochai 2y

    all I know is that baba is you

  37. @im_ali_pj 2y

    change your python

    1. @ColonelPhantom 2y

      What python is that? I tried it on python 3.10 and got the described result

      1. @im_ali_pj 2y

        i tried on 3.5 3.6 3.8 and 3.9

        1. @im_ali_pj 2y

          and 3.11

        2. @RiedleroD 2y

          try writing it to a file and executing it then, the precompile stage sometimes does a bit of optimization the interactive interpreter can't

  38. @colllapse 2y

    lol > This is guaranteed to be unique among simultaneously existing objects. such a scam

    1. @RiedleroD 2y

      ints aren't meant to be used like regular objects

  39. @colllapse 2y

    but that makes me wonder why its only 256 values

    1. @RiedleroD 2y

      because ints up to 256 are singletons (in CPython at least)

      1. @colllapse 2y

        i mean why not regular int but only char sized? everything bigger than byte eventually degrades to PyNumber?

        1. @RiedleroD 2y

          all python ints are objects. The interpreter just saves some time on creating lower ones, because they get used a lot in e.g. iterators and such

          1. @colllapse 2y

            I thought they start to become one after int32 boundaries passed. But I guess its only int16

            1. @RiedleroD 2y

              no, all of em are objects in python. They become slightly different objects at some point though, when values get too large (or small) they keep track of the value via a BigInt-ish structure internally.

              1. @RiedleroD 2y

                (iirc, afaik)

              2. @colllapse 2y

                I know that they become big int at some point (with enormous base as representation)

  40. @RiedleroD 2y

    it's an arbitrary decision. They could've made it 100 or 123. They still probably chose 256 because 2**8 is a nice number in computing

  41. @RiedleroD 2y

    ok I hate js as much as the next guy (or gal or nb pal) but if you modify your object inside its valueOf, that's your fault.

Use J and K for navigation