Python方法访问器会在每次访问时创建新对象? [英] Python method accessor creates new objects on each access?

查看:203
本文介绍了Python方法访问器会在每次访问时创建新对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在调查另一个问题时,我发现了以下内容:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆