Is " a is b " and " id(a) == id(b) " the same?

Alex Martelli aleax at aleax.it
Sun Mar 16 02:34:03 EST 2003


Chen wrote:

> Some one said that " a is b " is the same as " id(a) == id(b) ". But it
> seems not always true from the following codes:

Your observation is correct, and shows this common assertion needs
one extra qualification: "provided objects a and b are alive while
the whole operation is taking place".  This in turn comes from two
Python characteristics:

1. as soon as there is no reference extant to an object, Python is
   allowed to destroy that object (and classic Python does so at
   once, while other versions such as Jython may choose to delay);

2, id(x) is only guaranteed to be unique as long as x is alive -- as
   soon as x goes away, Python is allowed to "recycle" (i.e., reuse)
   the id.

The operation of "fetching an attribute", be it on a class a or
on an instance b of a, generates a new object on the fly when
the attribute being fetched is a function -- the object is a method,
unbound or bound, that wraps the function.  As soon as there are
no more references to that method-object (i.e. as soon as the call
to id(themethod) returns, unless other references have also been
bound to the method-object) it goes away and its id can be recycled.

So, in your example:


>>>> class a:
> ...     def f(self):
> ...         pass
> ...     g=f
> 
>>>> b=a()
>>>> print id(a.f), id(b.f), id(a.g), id(b.g)
> 6759528 6759528 6759528 6759528

all id's are the same just because each of the four method
object has gone away as soon as its only reference did, and
Python recycled the same id four times.  To show this, bind
another reference to the object being generated on the fly:

>>> froma = a.f
>>> fromb = b.f
>>> id(froma), id(fromb)
(1076642836, 1076643516)
>>> id(a.f), id(b.f)
(1076643476, 1076643476)
>>>

as we see, froma (one reference bound to a particular call
to a.f) and fromb (ditto but for b.f) differ -- indeed when
we fetch a.f again it gets ANOTHER id, because it's a new
and separate method object being built on the fly.

Another way to look at this is to add another class:

>>> class c:
...   def h(self): pass
...
>>> id(a.f), id(c.h)
(1076643476, 1076643476)
>>>

while a and c have nothing to do with each other, the same
"recycling effect" occurs -- a.f goes away and its id is
immediately recycled for c.h.

>>>> print a.f is b.f
> False
>>>> print a.f is a.g
> False
>>>> print b.f is b.g
> False
>>>> print a.g is b.f
> False

In all of these cases, the 'is' operator is keeping alive
its left operand until it's done executing, so there is no
"recycling effect" -- but each access to e.g. b.g and b.f
is generating a new separate method object, so the 'is'
ain't satisfied and returns False.


> Then, how to understand this example and the relationship of id() and
> 'is'?

By understanding the magic of method-objects -- which are
built on the fly.  If you use for attributes objects that
aren't functions, there is no transmogrification of their
types when you access them as attributes, no generation on
the fly, no "going away at once" of the generated objects,
and no occurrence of this fascinating anomaly you've found.


> I'm using 2.3a2 on win98, if it affects.

In this case it doesn't, but kudos for specifying this, as
often you see people failing to give this crucial info in
cases where it DOES matter.  I'm using 2.3 from CVS on
Linux, btw.


Alex





More information about the Python-list mailing list