Click to Visit Homepage : zzyzz.top


Bound Method and Unbound Method - 绑定方法 与 非绑定方法


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


 

posted @ 2017-09-07 14:47  zzYzz  阅读(517)  评论(0编辑  收藏  举报


Click to Visit Homepage : zzyzz.top