1

Following the SQLAlchemy tutorial, I hacked at my console and got following results:

>>> ed_user
<User(name='ed', fullname='Ed Jones', password='edspassword')>
>>> id(ed_user)
139859764807552
>>> our_user
<User(name='ed', fullname='Ed Jones', password='edspassword')>
>>> id(our_user)
139859764807552
>>> ed_user is our_user
True # so ed_user and our_user is the same object
>>> id(ed_user) - id(our_user)
0 # not suprisingly, their ids (i.e. their position in memory) don't differ
>>> id(ed_user) is id(our_user)
False # WAT?!
>>> id(ed_user) == id(our_user)
True # okay, as integer values they are the same
>>> id(id(ed_user)) == id(id(our_user))
True # but their id's ids are also the same

Why is id(ed_user) is id(our_user) False?

davidism
  • 121,510
  • 29
  • 395
  • 339
jan
  • 650
  • 1
  • 6
  • 17
  • 1
    In addition to answers, check also http://stackoverflow.com/questions/306313/pythons-is-operator-behaves-unexpectedly-with-integers – fredtantini Dec 01 '14 at 14:30

3 Answers3

4

You are creating integers, checking their id(), then discarding the integers again. Python then reuses the memory address for the next integer you created, so their id() values match.

From the id() function documentation:

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

Python object lifetimes are determined by their reference count; if there are more than 0 references to an object, it remains in memory, otherwise it is removed. The inner id() call in the expression id(id(our_user)) creates an integer that is only referenced by the stack; it is passed to the outer id() call and then removed from the stack, after which there are no references left to it and it is discarded again.

Had you created references to the produced integer values first, you'd have found they are not the same:

>>> a = b = []
>>> id(a) == id(b)
True
>>> a is b
True
>>> id_of_a, id_of_b = id(a), id(b)
>>> id_of_a == id_of_b
True
>>> id(id_of_a) == id(id_of_b)
False

But wait! For small integers (from -5 through to 256) Python only ever creates one copy (interning), so there the identity of two integers would be the same again:

>>> small = 200
>>> small is 200
True

All in all, unless you absolutely know you have to test for a singleton object (object identity), stick to equality tests instead. This goes for SQLAlchemy as well.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
1

Let’s go through your results one by one and see what’s going on:

>>> ed_user is our_user
True

So this is the situation. We have two variables, that reference the same object. Nothing special about that. You can even replicate that by just doing ed_user = our_user = 'something'.

>>> id(ed_user) == id(our_user)
True

Since both variables are identical, the return value of id() matches for both variables. Obviously then the difference is also zero.

>>> id(ed_user) is id(our_user)
False

Now this is where it get’s interesting. We know the return values match, but according to this check, the return values are not the same objects. id() returns an integer, so apparently those two integers are not identical. How is that possible? Well, it’s rather simple: Python only reserves fixed ids for a small set of small numbers. For all larger numbers, new integer objects (actual objects!) are created as they are needed. So in your case, the number is 139859764807552. That’s certainly a large number, so Python will create an integer object for the id(ed_user) call, … and for the id(our_user) call. So we have two different integer objects for this. And two different objects of course are never identical. So far so good.

>>> id(id(ed_user)) == id(id(our_user))
True

Now here it gets crazy. Above, we had two calls of id() that returned integer objects that both lived at the same time (since that was necessary for the comparison). Now, id() guarantees us to never return the same integer for two distinct objects that exist at the same time. This was the case in the previous call, and that’s why we ended up having two different integer objects (with the same value).

But here, everything works a bit differently: We are calling id(ed_user) first, this gives us an int object (let’s call it a) for the value 139859764807552. We then call id() on it and get an int object for the id of a; let’s call that id_a. At this point, a is no longer referenced, and also not needed for the comparisong (we are comparing id_a after all). So Python discards it. Next, we are calling id(out_user). This gives us an int object (b), also for the value 139859764807552. Since a is no longer around, b can go at the place of a, ending up with the same id. So we call id() on b and get back an integer object id_b. Since b is at the same memory position as a was, and since ids are based on memory position, id_a equals to id_b (and again id_a is id_b is not the case).

Community
  • 1
  • 1
poke
  • 369,085
  • 72
  • 557
  • 602
0

i think when you are extracting references then python picks a address nomber and throw it up when it is used and pick another one when you try to extract again. check here

In [71]: a = b = []

In [72]: a is b
Out[72]: True

In [73]: id(a) == id(b)
Out[73]: True

In [74]: id(a) - id(b)
Out[74]: 0

In [75]: id(a) is id(b)
Out[75]: False

In [76]: id(id(a)), id(id(b))
Out[76]: (45539872, 45539872)

In [77]: id(id(a))#--
Out[77]: 45540016   !
                    ! see here its picking another reference then it pick before
In [78]: id(id(b))#--!
Out[78]: 45539896


In [81]: id(id(a)), id(id(b)) # but here it's again same
Out[81]: (45539824, 45539824)

In [82]: id(id(a)) == id(id(b))
Out[82]: True

In [83]: id(a) is id(b)
Out[83]: False
Vishnu Upadhyay
  • 5,043
  • 1
  • 13
  • 24