Python 字节码函数调用传递自
Python bytecode function call passing self
我正在尝试了解字节码的工作原理。
a.func()
是一个函数调用。对应的字节码大致是LOAD_GLOBAL a
、LOAD_ATTR attr
然后是CALL_FUNCTION
0个参数。
如果 a
是一个模块,这完全没问题。但是如果 a
是一个对象,它必须传递对象实例本身。由于 Python 在编译时无法知道 a
是模块还是对象,因此无论 a
的类型如何,字节码自然都是相同的。但是如果 a
是一个对象,运行时系统如何处理 self
作为 func
的第一个参数呢?字节码级别以下是否有一些特殊处理,表示 "if it is called on an object prepend the object as the first argument"?
LOAD_ATTR 通过描述符(https://docs.python.org/2/howto/descriptor.html)施展魔法。
假设 a 是 class A 的对象:
在 python 中函数是描述符。当您执行 a.func 时,实际上它是 returns A.func
,这是描述符对象(未绑定函数)。然后它 "upgrades" 本身绑定函数(调用 A.func.__get__
)。未绑定的函数必须首先显式地给出 self 参数。绑定函数本身已经记住了 self 参数 "inside"。
在python中模块是一个对象并且使用完全相同的机制。
简而言之,a.func
已经知道它绑定到哪个对象,因此不需要明确的 self
(它已经知道 self
是什么):
>>> a.func
<bound method A.func of <__main__.A object at 0x10e810a10>>
将此与 A.func
对比(其中 A
是 class):
>>> A.func
<unbound method A.func>
调用 A.func
确实需要显式 self
:
>>> A.func()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method func() must be called with A instance as first argument (got nothing instead)
>>> A.func(a)
>>>
或者,在字节码中:
0 LOAD_GLOBAL 0 (A)
3 LOAD_ATTR 1 (func)
6 LOAD_GLOBAL 2 (a)
9 CALL_FUNCTION 1
12 POP_TOP
(注意多余的LOAD_GLOBAL
。)
Python Language Reference 中解释了绑定与未绑定方法的机制(搜索 im_self
或 __self__
)。
字节码不必因不同的对象类型而不同。管理 绑定行为 是对象类型本身的责任。 descriptor protocol.
中对此进行了介绍
简而言之,LOAD_ATTR
通过 object.__getattribute__
hook:
委托对对象的属性访问
Called unconditionally to implement attribute accesses for instances of the class.
对于模块,__getattribute__
只需在 __dict__
命名空间中查找名称并 return 即可。但是对于 类 和 meta类,如果属性支持,实现将调用描述符协议。函数支持描述符协议和 return 一个 绑定方法 当被问到时:
>>> class Foo:
... def method(self): pass
...
>>> Foo().method # access on an instance -> binding behaviour
<bound method Foo.method of <__main__.Foo object at 0x107155828>>
>>> Foo.method # access on the class, functions just return self when bound here
<function Foo.method at 0x1073702f0>
>>> Foo.method.__get__(Foo(), Foo) # manually bind the function
<bound method Foo.method of <__main__.Foo object at 0x107166da0>>
这种绑定行为也是 property
, classmethod
and staticmethod
对象如何工作的基础(后者通过 return 函数本身消除了函数的绑定行为)。
我正在尝试了解字节码的工作原理。
a.func()
是一个函数调用。对应的字节码大致是LOAD_GLOBAL a
、LOAD_ATTR attr
然后是CALL_FUNCTION
0个参数。
如果 a
是一个模块,这完全没问题。但是如果 a
是一个对象,它必须传递对象实例本身。由于 Python 在编译时无法知道 a
是模块还是对象,因此无论 a
的类型如何,字节码自然都是相同的。但是如果 a
是一个对象,运行时系统如何处理 self
作为 func
的第一个参数呢?字节码级别以下是否有一些特殊处理,表示 "if it is called on an object prepend the object as the first argument"?
LOAD_ATTR 通过描述符(https://docs.python.org/2/howto/descriptor.html)施展魔法。
假设 a 是 class A 的对象:
在 python 中函数是描述符。当您执行 a.func 时,实际上它是 returns A.func
,这是描述符对象(未绑定函数)。然后它 "upgrades" 本身绑定函数(调用 A.func.__get__
)。未绑定的函数必须首先显式地给出 self 参数。绑定函数本身已经记住了 self 参数 "inside"。
在python中模块是一个对象并且使用完全相同的机制。
简而言之,a.func
已经知道它绑定到哪个对象,因此不需要明确的 self
(它已经知道 self
是什么):
>>> a.func
<bound method A.func of <__main__.A object at 0x10e810a10>>
将此与 A.func
对比(其中 A
是 class):
>>> A.func
<unbound method A.func>
调用 A.func
确实需要显式 self
:
>>> A.func()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method func() must be called with A instance as first argument (got nothing instead)
>>> A.func(a)
>>>
或者,在字节码中:
0 LOAD_GLOBAL 0 (A)
3 LOAD_ATTR 1 (func)
6 LOAD_GLOBAL 2 (a)
9 CALL_FUNCTION 1
12 POP_TOP
(注意多余的LOAD_GLOBAL
。)
Python Language Reference 中解释了绑定与未绑定方法的机制(搜索 im_self
或 __self__
)。
字节码不必因不同的对象类型而不同。管理 绑定行为 是对象类型本身的责任。 descriptor protocol.
中对此进行了介绍简而言之,LOAD_ATTR
通过 object.__getattribute__
hook:
Called unconditionally to implement attribute accesses for instances of the class.
对于模块,__getattribute__
只需在 __dict__
命名空间中查找名称并 return 即可。但是对于 类 和 meta类,如果属性支持,实现将调用描述符协议。函数支持描述符协议和 return 一个 绑定方法 当被问到时:
>>> class Foo:
... def method(self): pass
...
>>> Foo().method # access on an instance -> binding behaviour
<bound method Foo.method of <__main__.Foo object at 0x107155828>>
>>> Foo.method # access on the class, functions just return self when bound here
<function Foo.method at 0x1073702f0>
>>> Foo.method.__get__(Foo(), Foo) # manually bind the function
<bound method Foo.method of <__main__.Foo object at 0x107166da0>>
这种绑定行为也是 property
, classmethod
and staticmethod
对象如何工作的基础(后者通过 return 函数本身消除了函数的绑定行为)。