Python方法访问器会在每次访问时创建新对象? [英] Python method accessor creates new objects on each access?
问题描述
在调查另一个问题时,我发现了以下内容:
When investigating for another question, I found the following:
>>> class A:
... def m(self): return 42
...
>>> a = A()
这是预期的:
>>> A.m == A.m
True
>>> a.m == a.m
True
但是我没有想到 :
>>> a.m is a.m
False
尤其不是这个:
>>> A.m is A.m
False
Python似乎为每个方法访问创建了新对象.为什么我会看到这种行为? IE.为什么不能重用每个类一个对象和每个实例一个对象的原因是什么?
Python seems to create new objects for each method access. Why am I seeing this behavior? I.e. what is the reason why it can't reuse one object per class and one per instance?
推荐答案
是的,Python会为每次访问创建新的方法对象,因为它会构建包装器对象以传入self
.这称为绑定方法.
Yes, Python creates new method objects for each access, because it builds a wrapper object to pass in self
. This is called a bound method.
Python使用描述符来执行此操作;函数对象具有__get__
方法,该方法在类上访问时将被调用:
Python uses descriptors to do this; function objects have a __get__
method that is called when accessed on a class:
>>> A.__dict__['m'].__get__(A(), A)
<bound method A.m of <__main__.A object at 0x10c29bc10>>
>>> A().m
<bound method A.m of <__main__.A object at 0x10c3af450>>
请注意,Python无法重用A().m
; Python是一种高度动态的语言,访问.m
的行为可能触发更多代码,这可能会更改A().m
下次访问时返回的行为.
Note that Python cannot reuse A().m
; Python is a highly dynamic language and the very act of accessing .m
could trigger more code, which could alter behaviour of what A().m
would return next time when accessed.
@classmethod
和@staticmethod
装饰器利用此机制分别返回绑定到该类的方法对象和一个普通的未绑定函数:
The @classmethod
and @staticmethod
decorators make use of this mechanism to return a method object bound to the class instead, and a plain unbound function, respectively:
>>> class Foo:
... @classmethod
... def bar(cls): pass
... @staticmethod
... def baz(): pass
...
>>> Foo.__dict__['bar'].__get__(Foo(), Foo)
<bound method type.bar of <class '__main__.Foo'>>
>>> Foo.__dict__['baz'].__get__(Foo(), Foo)
<function Foo.baz at 0x10c2a1f80>
>>> Foo().bar
<bound method type.bar of <class '__main__.Foo'>>
>>> Foo().baz
<function Foo.baz at 0x10c2a1f80>
有关更多详细信息,请参见 Python描述符操作方法.
See the Python descriptor howto for more detail.
但是,Python 3.7添加了一个新的LOAD_METHOD
-CALL_METHOD
操作码对,以精确地替换当前的LOAD_ATTRIBUTE
-CALL_FUNCTION
操作码对,以避免每次都创建一个新的方法对象.此优化将instance.foo()
的执行路径从type(instance).__dict__['foo'].__get__(instance, type(instance))()
转换为type(instance).__dict__['foo'](instance)
,因此手动"将实例直接传递给函数对象.如果找到的属性不是纯python函数对象,则优化将退回到常规属性访问路径(包括绑定描述符).
However, Python 3.7 adds a new LOAD_METHOD
- CALL_METHOD
opcode pair that replaces the current LOAD_ATTRIBUTE
- CALL_FUNCTION
opcode pair precisely to avoid creating a new method object each time. This optimisation transforms the executon path for instance.foo()
from type(instance).__dict__['foo'].__get__(instance, type(instance))()
with type(instance).__dict__['foo'](instance)
, so 'manually' passing in the instance directly to the function object. The optimisation falls back to the normal attribute access path (including binding descriptors) if the attribute found is not a pure-python function object.
这篇关于Python方法访问器会在每次访问时创建新对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!