this is with CPython 2.7.2 and 3.2.2.
Suppose we define Class
and obj
as follows.
class Class(object): def m(self): pass @property def p(self): return None @staticmethod def s(): pass obj = Class()
short version
why does the following code print False
for each print()
?
print(Class.__dict__ is Class.__dict__) print(Class.__subclasshook__ is Class.__subclasshook__) print(Class.m is Class.m) print(obj.__delattr__ is obj.__delattr__) print(obj.__format__ is obj.__format__) print(obj.__getattribute__ is obj.__getattribute__) print(obj.__hash__ is obj.__hash__) print(obj.__init__ is obj.__init__) print(obj.__reduce__ is obj.__reduce__) print(obj.__reduce_ex__ is obj.__reduce_ex__) print(obj.__repr__ is obj.__repr__) print(obj.__setattr__ is obj.__setattr__) print(obj.__sizeof__ is obj.__sizeof__) print(obj.__str__ is obj.__str__) print(obj.__subclasshook__ is obj.__subclasshook__) print(obj.m is obj.m)
(that in Python 2 for Python 3 omit print()
for Class.m
and add similar ones for obj.__eq__
, obj.__ge__
, obj.__gt__
, obj.__le__
, obj.__lt__
and obj.__ne__
)
and why, on the contrary, prints the following True
code for each print()
?
print(Class.__class__ is Class.__class__) print(Class.__delattr__ is Class.__delattr__) print(Class.__doc__ is Class.__doc__) print(Class.__format__ is Class.__format__) print(Class.__getattribute__ is Class.__getattribute__) print(Class.__hash__ is Class.__hash__) print(Class.__init__ is Class.__init__) print(Class.__module__ is Class.__module__) print(Class.__new__ is Class.__new__) print(Class.__reduce__ is Class.__reduce__) print(Class.__reduce_ex__ is Class.__reduce_ex__) print(Class.__repr__ is Class.__repr__) print(Class.__setattr__ is Class.__setattr__) print(Class.__sizeof__ is Class.__sizeof__) print(Class.__str__ is Class.__str__) print(Class.__weakref__ is Class.__weakref__) print(Class.p is Class.p) print(Class.s is Class.s) print(obj.__class__ is obj.__class__) print(obj.__dict__ is obj.__dict__) print(obj.__doc__ is obj.__doc__) print(obj.__module__ is obj.__module__) print(obj.__new__ is obj.__new__) print(obj.__weakref__ is obj.__weakref__) print(obj.p is obj.p) print(obj.s is obj.s)
(that in Python 2 for Python 3 add similar print()
for Class.__eq__
, Class.__ge__
, Class.__gt__
, Class.__le__
, Class.__lt__
and Class.__ne__
and Class.m
)
long version
if we request id(obj.m)
twice in a row, then we get the same ID twice.
>>> id(obj.m) 139675714789856 >>> id(obj.m) 139675714789856
but if we ask for id(obj.m)
, then some expressions containing links to obj.m
, then id(obj.m)
again, the identifier changes sometimes (but not always). if it changes, then if we ask for id(obj.m)
at another time, the identifier returns to its original value (but not always). if it doesn't change backwards, then it seems that repeating intermediate expressions makes the identifier alternate between the two values.
Here are a few examples in which id(obj.m)
does not change:
>>> print(obj.m); id(obj.m) <bound method Class.m of <__main__.Class object at 0x7f08c96058d0>> 139675714789856 >>> obj.m is None; id(obj.m) False 139675714789856 >>> obj.m.__func__.__name__; id(obj.m) 'm' 139675714789856 >>> obj.m(); id(obj.m) 139675714789856
Here is an example in which id(obj.m)
changes and then comes back:
>>> obj.m; id(obj.m); id(obj.m) <bound method Class.m of <__main__.Class object at 0x7f08c96058d0>> 139675715407536 139675714789856
here is an example in which id(obj.m)
changes and then does not change:
>>> obj.m is obj.m; id(obj.m); id(obj.m) False 139675715407536 139675715407536
here is the same expression repeated several times to demonstrate variable behavior:
>>> obj.m is obj.m; id(obj.m); id(obj.m) False 139675714789856 139675714789856 >>> obj.m is obj.m; id(obj.m); id(obj.m) False 139675715407536 139675715407536 >>> obj.m is obj.m; id(obj.m); id(obj.m) False 139675714789856 139675714789856
so my whole question
- What attributes can change their identity as a side effect of expressions that do not change these attributes?
- what expressions cause such changes?
- What is the mechanism that causes such changes?
- Under what conditions are past identifiers returned?
- Why is this not the first identification processed indefinitely, which would avoid all this complication?
- Is this documented?