Tharindu Krishan
Tharindu Krishan

Reputation: 177

How can two Python objects have same id but 'is' operator returns False?

id(t+t), id(t*2)
(42838592, 42838592)

(t+t) is (t*2)
False

If two variable point to same object 'is' operator will return true.But first line said both have same id but 'is' operator give false value.

Upvotes: 7

Views: 3843

Answers (2)

abarnert
abarnert

Reputation: 365667

As explained by Ned Batchelder's answer, and by the docs for the id function:

Two objects with non-overlapping lifetimes may have the same id() value.

And the two objects have non-overlapping lifetimes.

The fact that they're part of the same tuple expression doesn't change that, because it's not t+t and t*2 that are part of a tuple, it's id(t+t) and id(t*2). So, those two integer values returned by id have an overlapping lifetime, but the arguments passed to id do not.

One way to understand this is to look at how CPython compiles the code:

>>> dis.dis('id(t+t), id(t*2)')
  1           0 LOAD_NAME                0 (id)
              2 LOAD_NAME                1 (t)
              4 LOAD_NAME                1 (t)
              6 BINARY_ADD
              8 CALL_FUNCTION            1
             10 LOAD_NAME                0 (id)
             12 LOAD_NAME                1 (t)
             14 LOAD_CONST               0 (2)
             16 BINARY_MULTIPLY
             18 CALL_FUNCTION            1
             20 BUILD_TUPLE              2
             22 RETURN_VALUE

So, here's what happens. (I'm going to pick a value for t, say 1000, just to make this a bit easier to follow.)

  • id, 1000, and 1000 get pushed on the stack.
  • BINARY_ADD creates a 2000 value at location 42838592.
  • id gets called on that value and returns a 42838592 value at location, say, 42838616.
  • Since the 42838592 value is no longer on the stack and wasn't stored anywhere, id's parameter was the only reference to it, so when it gets decrefed at the end of the function, it's immediately deleted.
  • id, 1000, and 2 get pushed on the stack.
  • BINARY_MULTIPLY creates a new 2000 object. Since location 42838592 was just returned to the object pool, the new value reuses that location.
  • id returns another 42838592, this time at location, say, 42838640.

So, the two int values 4283592 and 4283592 have overlapping lifetimes (and the first one overlaps with the second 2000), the two 2000s do not overlap.


And finally, notice that if t is a small number,

>>> t = 2
>>> (t+t) is (t*2)
True

… because all 4 values (except in unusual cases) are references to the same object.

And meanwhile, if t is a constant rather than a variable, 1000+1000 is 1000*2 may or may not be true, depending on your CPython version, because of the way constant folding within a compilation unit works.

All of which goes to show that trying to actually take advantage of whether two equal ints are or are not the same object is pretty much always a terrible idea. The only reason you should ever care about this question is if you're trying to learn more about the internals of CPython.


And of course this is all CPython-specific. Most other Python interpreters use some form of garbage collector instead of refcounting, so the first 2000 is unlikely to be destroyed before the second one is created. Plus, not all of them use an object pool like CPython. Not to mention that they're allowed to do completely different things for id as long as they can guarantee unique values for non-overlapping objects.

PyPy will actually usually return the same value here—but only because it folds t+t and t*2 into the same object in the first place; try it with t+t and t*3and you'll get completely differentid`s.

Upvotes: 7

Ned Batchelder
Ned Batchelder

Reputation: 375514

In the first example, your objects don't overlap in time: one is created then destroyed, then another is created with the same id.

When you compare them with is, you are holding onto both objects, so they get different ids.

Upvotes: 11

Related Questions