pythonpython-2.7identityequality

`id` function in Python 2.7, `is` operator, object identity and user-defined methods


The results from the code below in Python 2.7 struck me as a contradiction. The is operator is supposed to work with object identity and so is id. But their results diverge when I'm looking at a user-defined method. Why is that?

py-mach >>class Hello(object):
...  def hello():
...    pass
...
py-mach >>Hello.hello is Hello.hello
False
py-mach >>id(Hello.hello) - id(Hello.hello)
0

I found the following excerpt from the description of the Python data model somewhat useful. But it didn't really make everything clear. Why does the id function return the same integer if the user-defined method objects are constructed anew each time?

User-defined method objects may be created when getting an attribute of a class (perhaps via an instance of that class), if that attribute is a user-defined function object, an unbound user-defined method object, or a class method object. When the attribute is a user-defined method object, a new method object is only created if the class from which it is being retrieved is the same as, or a derived class of, the class stored in the original method object; otherwise, the original method object is used as it is.


Solution

  • The Python documentation for the id function states:

    Return the "identity" of an object. This is an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.

    (emphasis mine)

    When you do id(Hello.hello) == id(Hello.hello), the method object is created only briefly and is considered "dead" after the first call to 'id'. Because of the call to id, you only need Hello.hello to be alive for a short period of time -- enough to obtain the id. Once you get that id, the object is dead and the second Hello.hello can reuse that address, which makes it appear as if the two objects have the same id.

    This is in contrast to doing Hello.hello is Hello.hello -- both instances have to live long enough to be compared to each other, so you end up having two live instances.

    If you instead tried:

    >>> a = Hello.hello
    >>> b = Hello.hello
    >>> id(a) == id(b)
    False
    

    ...you'd get the expected value of False.