43

The id() inbuilt function gives...

an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime.

The is operator, instead, gives...

object identity

So why is it possible to have two objects that have the same id but return False to an is check? Here is an example:

>>> class Test():
...   def test():
...     pass
>>> a = Test()
>>> b = Test()
>>> id(a.test) == id(b.test)
True
>>> a.test is b.test
False

A more troubling example: (continuing the above)

>>> b = a
>>> b is a
True
>>> b.test is a.test
False
>>> a.test is a.test
False

However:

>>> new_improved_test_method = lambda: None
>>> a.test = new_improved_test_method
>>> a.test is a.test
True
badp
  • 11,409
  • 3
  • 61
  • 89
  • 2
    It's not like I _need_ this for an actual program or metaclass jedi mind trick, mind you. I was bored on the train and I tried that :) – badp May 25 '10 at 15:41
  • 1
    I think the important question is why `a.test is a.test` is False. Once you know that the rest should make sense... – Skilldrick May 25 '10 at 15:44
  • The oil spill is fine and dandy and it's coming to a city near you Real Soon! Maybe you should organize some get together to celebrate its coming! – badp May 25 '10 at 15:55
  • == and is are completely independent. Here's a case where is does not imply equality. `a = float('nan'); print a is a, a == a` –  May 25 '10 at 17:32
  • 2
    @Paul: I didn't ask about `a == b`, I asked about `id(a) == id(b)` :) – badp May 25 '10 at 19:17
  • Related (not duplicate): *[Why does comparing strings using either '==' or 'is' sometimes produce a different result?](https://stackoverflow.com/questions/1504717/why-does-comparing-strings-using-either-or-is-sometimes-produce-a-differe)* – Peter Mortensen Jan 06 '23 at 18:18

1 Answers1

66
>>> b.test is a.test
False
>>> a.test is a.test
False

Methods are created on-the-fly each time you look them up. The function object (which is always the same object) implements the descriptor protocol and its __get__ creates the bound method object. No two bound methods would normally be the same object.

>>> id(a.test) == id(b.test)
True
>>> a.test is b.test
False

This example is deceptive. The result of the first is only True by coincidence. a.test creates a bound method and it's garbage collected after computing id(a.test) because there aren't any references to it. (Note that you quote the documentation saying that an id is "unique and constant for this object during its lifetime" (emphasis mine).) b.test happens to have the same id as the bound method you had before and it's allowed to because no other objects have the same id now.

Note that you should seldom use is and even less often use id. id(foo) == id(bar) is always wrong.


Regarding your new example, hopefully you get what it does now:

>>> new_improved_test_method = lambda: None
>>> a.test = new_improved_test_method
>>> a.test is a.test
True

In this case, we aren't making methods on the fly from functions on the class automatically binding self and returning bound method objects. In this case, you simply store a function as an instance attribute. Nothing special happens on lookup (descriptors only get called when you look up a class attribute), so every time you look up the attribute, you get the original object you stored.

Mike Graham
  • 73,987
  • 14
  • 101
  • 130
  • 10
    *"Methods are created on-the-fly each time you look them up. The function object (which is always the same object) implements the descriptor protocol and its `__get__` creates the bound method object. No two bound methods would normally be the same object."* Ha, that's news to me. Nice! – Will McCutchen May 25 '10 at 16:00