6

Running:

a = 257
b = 257
print id(a) == id(b)

Results in:

same statement enter image description here

Same statement but opposite results. Why?

9
  • 1
    Why are you comparing integers by ID? Commented Apr 7, 2016 at 11:32
  • 1
    see : stackoverflow.com/questions/3402679/… interpreter might not cache those number, because it does not know if you're going to reuse it Commented Apr 7, 2016 at 11:33
  • 2
    @Hacketo: That's different. This question is about two variables that are equivalent integers having the same id when run from a file, but different id's when run in the shell. Commented Apr 7, 2016 at 11:35
  • 1
    @jonrsharpe It is a special number. Values between -5 and 256 are preallocated. So I test 257 Commented Apr 7, 2016 at 11:38
  • One point is that python has some kind of small memory of range of integer numbers, and they always will have same id. But in this case, it's really something weird Commented Apr 7, 2016 at 11:38

1 Answer 1

10

The code in test.py is parsed together which is more optimizable than the code parsed as separate statements in the interpreter

When you put it in a test.py and run it as a whole, the byte code compiler has a better chance of analyzing the usage of literals and optimizing them. (Hence you get a and b pointing to the same place)

As opposed to when you run the separate statements (parsed separately) in the interpreter (where I think it only optimizes up to 256 but not 257 via preallocation)

Play with this in the interpreter to see the effect of separate statements:

>>> a, b = 257, 257      # or if you prefer: a = 257; b = 257
>>> print a is b
True

>>> a = 257
>>> b = 257
>>> print a is b
False

Defining a function in the interperter also gives it a change to analyze and optimize the used literals

>>> def test():
...     a = 257
...     b = 257
...     print a is b
... 
>>> test()
True

This optimization is not limited to integers only, works e.g. for floats too (floats are not subject to a cache like integers in the [-5, 256] range are)

>>> def test():
...     pi = 3.14
...     x = 3.14
...     return x is pi
... 
>>> test()
True

# As opposed to separate statements:
>>> pi = 3.14
>>> x = 3.14
>>> x is pi
False

Looking at the byte code to see that it indeed reuses the same constant

>>> dis.dis(test)
  2           0 LOAD_CONST               1 (3.14)
              3 STORE_FAST               0 (pi)

  3           6 LOAD_CONST               1 (3.14) <-- Same constant 1 reused
              9 STORE_FAST               1 (x)

  4          12 LOAD_FAST                1 (x)
             15 LOAD_FAST                0 (pi)
             18 COMPARE_OP               8 (is)
             21 RETURN_VALUE  
Sign up to request clarification or add additional context in comments.

2 Comments

From the comments, the OP knows that "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." from the docs.
I'm with you, but can we dig more ? :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.