Bound Method and Unbound Method
通常有两种方法对类的方法(instance.method)/属性(class.attribute)进行引用, 一种称做 Bound Method,
即通过类的类的实例对象进行引用(instance.foo)。引用区别于调用, 引用为 instance.foo 返回的是方法对象
(PyFunctionObject/PyMethodObject); 而调用返回的是方法的运行结果。另一种引用形式叫做 Unboud Method,
是直接通过类进行属性引用, 即 class.foo 的形式进行引用。
在 python 中, Bound Method 和 Unbound Method 的本质区别在于 是否存在类的 instance 对象绑定到
PyFunctionObject 中(这里用 '中' 是想说明 PyFunctionObject 是一个结构体,我把它想象成一个容器的形象)。
因此, 在对 Unbound Method 进行调用的时候必须显式地传一个实例对象, 比如 class.UnboundMethod(instance),
所以说在类中所定义函数的第一个位置参数 self 代表是调用该函数的实例。 然而, 对 python 虚拟机来说, 无论是对
Bound Method 还是对 Unbound Method 进行调用, 其中本质上是一样的,都是调用带位置参数的普通函数。
其中的区别在于, 当调用 Bound Method 的时候,虚拟机自主完成了 instance 和 PyFunctionObject 的绑定;
而调用 Unbound Method 的时候虚拟机无法做绑定的动过,因而调用 Unbound Method 的时候需要显式地传入所属类的 instance.
对于通过 Bound Method 调用成员函数来说,每一次函数调用都会触发一次绑定过程。原因在于每次进行引用的时候都会重新获得
PyFunctionObject(description),进而新的 PyMethodObject 对象。
例子,
1 class A(object):
2 def foo(self):
3 print "haha - " + self.foo.__name__
4
5 print (A.__dict__) #1
6
7 abc = A()
8
9 print (A.foo) #2
10
11 print(A.foo.im_self) #3
12
13 print (abc.foo) #4
14
15 print (abc.foo.im_self,abc.foo.im_func, #5
16 abc.foo.im_class,abc.foo.__name__)
17
18 abc.foo() #6
19 try:
20 print A.foo() #7
21 except TypeError as e:
22 print e
23
24 A.foo(abc) #7
通过 dis 模块获取到示例代码的部分字节码,
31 LOAD_NAME 1 (A)
34 LOAD_ATTR 3 (foo)
37 PRINT_ITEM
其中关键在 '34 LOAD_ATTR' 指令一行, LOAD_ATTR 指令最终会调用 type_getattro。
而 type_getattro 会在 class A 对象的 tp_dict (__dict__ 属性) 中找到 function foo 对应的 PyFunctionObject.
因为 PyFunctionObject 是一个 descriptor,所以它会调用其 __get__ 函数. 最终,虽然 A.foo(class.foo, Unbound Method)
也得到了一个 PyMethodObject, 但是其中 im_self 属性却是 None.
示例代码的打印输出,
Output,
{... 'foo': <function foo at 0x00000000033A2D68>, '__weakref__': <attribute '__weakref__' of 'A' objects>, ...}
#1 type_getattro 会在 class A 对象的 tp_dict (__dict__ 属性) 中找到 function foo 对应的 PyFunctionObject.
<unbound method A.foo> #2 通过类的引用(class.foo) 为 Unbound Method
None #3 虽然 A.foo(class.foo, Unbound Method)也得到了一个 PyMethodObject, 但是其中 im_self 属性却是 None.
<bound method A.foo of <__main__.A object at 0x000000000330AE80>> #4 通过实例的引用(instance.foo) 为 Bound Method
(<__main__.A object at 0x000000000330AE80>, <function foo at 0x00000000033A2D68>, <class '__main__.A'>, 'foo')
#5 abc.foo(instance.foo, Bound Method)得到的 PyMethodObject 中 im_self 和 im_func 属性分别是 __main__.A 实例对象 和 foo 函数对象.
haha - foo #6 通过实例调用 foo
unbound method foo() must be called with A instance as first argument (got nothing instead) #7 Unbound Method 的调用需要显式地传入实例 instance 对象.
haha - foo