我试图理解字节码是如何工作的。
a.func()
是函数调用。相应的字节码大致为LOAD_GLOBAL a
,LOAD_ATTR attr
,然后是CALL_FUNCTION
,带有0个参数。
如果a
是一个模块,这完全没问题。但是如果a
是一个对象,它必须传递对象实例本身。由于Python在编译时无法知道a
是模块还是对象,因此无论a
的类型如何,字节码都是相同的。但是如果self
是一个对象,运行时系统如何处理func
作为a
的第一个参数?在字节码级别下面是否有一些特殊处理说"如果在一个对象上调用它,则将该对象作为第一个参数"?
答案 0 :(得分:2)
字节码不必因不同的对象类型而异。对象类型本身负责管理绑定行为。这包含在descriptor protocol。
中简而言之,LOAD_ATTR
代理通过object.__getattribute__
hook属性访问对象:
无条件调用以实现类实例的属性访问。
对于模块,__getattribute__
只需查找__dict__
命名空间中的名称并返回它。但是对于类和元类,如果属性支持,实现将调用描述符协议。函数支持描述符协议,并在询问时返回绑定方法:
>>> 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
和staticmethod
对象工作方式的基础(后者通过返回函数本身来实现函数的绑定行为)。
答案 1 :(得分:1)
LOAD_ATTR通过描述符(https://docs.python.org/2/howto/descriptor.html)完成魔术。
假设a是A类的对象:
在python函数中是描述符。当你执行a.func时,实际上它返回A.func
,这是描述符对象(未绑定的函数)。然后&#34;升级&#34;本身绑定函数(A.func.__get__
被调用)。必须首先明确地给出未绑定函数的自参数。绑定函数已经记住了自我论证&#34;内部&#34;本身。
在python模块中是一个对象并使用完全相同的机制。
答案 2 :(得分:1)
简而言之,a.func
已经知道它绑定了哪个对象,因此不需要显式的self
(它已经知道self
是什么):
>>> a.func
<bound method A.func of <__main__.A object at 0x10e810a10>>
将此与A.func
(其中A
为类)对比:
>>> 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__
)解释了绑定与未绑定方法的机制。