Here is the simple Python code: What's the difference between Case 1 and Case 2 -- why am I getting result as False in first case and True in other? Why are the id
s equal in the Case 2? Also does dir(object)
call object._dir__()
internally? If so the return object/results of two calls should it be the same.
class Hello:
def __init__(self):
self.a1 = "a1"
hello = Hello()
print(hello)
# Case 1
var1 = dir(hello)
var2 = hello.__dir__()
print(id(var1), id(var2), id(var1) == id(var2))
# Case 2
print(id(dir(hello)), id(hello.__dir__()), id(dir(hello)) == id(hello.__dir__()))
print(dir(hello) == hello.__dir__())
Output
<__main__.Hello object at 0x7f320828c320>
139852862206472 139852862013960 False
139852862014024 139852862014024 True
False
It's just a coincidence that you're ever getting True
. (Well, not a coincidence, since the implementation of CPython makes it very likely… but it's not something the language requires.)
In case 1, you have two different dict
s in var1
and var2
. They're both alive at the same time, so they can't have the same id
.
In case 2, you again have two different dict
s—but this time, you aren't storing them anywhere; as soon as you call id
on one, you release it, which means it can get garbage collected* before you get the other one,** which means it can end up reusing the same id
.***
Notice that the docs for id
say:
This is an 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.
If you actually want to test whether two expressions refer to the same object, use is
, don't compare their id
s.
Your edited question also asks:
Also does dir(object) calls object._dir__() internally?
According to dir
:
If the object has a method named
__dir__()
, this method will be called and must return the list of attributes.
And the data model section on __dir__
says:
Called when
dir()
is called on the object. A sequence must be returned.dir()
converts the returned sequence to a list and sorts it.
Then you say:
If so the return object of two calls should be the same.
Well, it depends on what you mean by "the same". It should return an equal value (since nothing has changed), but it's not going to be the identical value, which is what you're trying to test for. (If it isn't obvious why dir
gives you a new list each time, it should still be clear that it must do so from the fact that "dir()
converts the returned sequence to a list and sorts it"…)
* Because CPython uses reference counting as its primary garbage collection mechanism, "can be collected" generally means "will be collected immediately". This isn't true for most other Python implementations.
** If the order in which parts of your expression get evaluated isn't clear to you from reading the docs, you can try dis.dis('id(dir(hello)) == id(hello.__dir__())')
to see the actual bytecodes in order.
*** In CPython, the id
is just the address of the PyObject
struct that represents the object; if one PyObject
gets freed and another one of the same type gets allocated immediately after, it will usually get the same address.