Why do some expressions that reference `` xy`` change `` id (xy) ``?

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?
+6
source share
2 answers

What attributes can change their identity as a side effect of expressions that do not change these attributes?

Properties, or rather, objects that implement the descriptor protocol . For example, Class.__dict__ not a dict , but a dictproxy . Obviously, this object is regenerated every time it is requested. What for? It is likely to reduce the overhead of creating an object until it is needed. However, this is an implementation detail. The important thing is that __dict__ works as documented.

Even ordinary instance methods are handled using descriptors, which explains why obj.m is not obj.m Interestingly, if you do obj.m = obj.m , you permanently save this shell of the method in the instance, and then obj.m is obj.m

what expressions cause such changes?

Any access to an attribute can trigger the __get__() descriptor method, and this method can always return the same object or return different values ​​each time.

What is the mechanism that causes such changes?

Properties / descriptors.

Under what conditions do past identities return?

Not sure what you mean by "recycling." Do you mean get rid or reuse? In CPython, the id object of an object is its memory location. If two objects fall into the same memory cell at different times, they will have the same id . Therefore, two links that have the same id value at different times (even within the same operator) are not necessarily the same object. Other Python implementations use different rules to generate id s. For example, I believe that Jython uses incremental integers that provide greater clarity in the identity of objects.

why not the first identifier processed endlessly to avoid all this complication?

There was supposedly some benefit in using descriptors. The source code for the Python interpreter is available; look if you want to know more details.

any of these documented?

Not. These are details specific to the implementation of the CPython interpreter, and cannot be relied on. Other Python implementations (including future versions of CPython) can and are likely to behave differently. There are significant differences between 2.x and 3.x CPython, for example.

+5
source

When you write xy, a related or unrelated method is created. This is a new facility. It can work anywhere in memory. If you write xy and do not use the result, its refcnt may go to zero. This means that memory is available and can be used by the next xy, possibly in the same place, but not necessarily.

Please note that CPython provides very few guarantees of object identity (i.e. you are guaranteed that there is only one instance of None); otherwise, much of what you see is arbitrary implementation options that can change.

+2
source

Source: https://habr.com/ru/post/913583/


All Articles