# 从字节码看实例方法

有类定义和实例对象以及测试如下,很明显可以注意到所有方法都是定义在类的属性空间中的,并且当我们从实例和类的角度去尝试查看一个“实例方法”的本质是发现从实例的角度出发其不再是我们定义的函数对象,而是一个bound method​对象。

class Point:
    z = 0
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def distance(self):
        return math.sqrt(self.x*self.x + self.y*self.y)
p = Point(1, 2)

>>> c.p
<classe.Point object at 0x000001F18395DAC0>
>>> c.Point
<class 'classe.Point'>

>>> c.p.distance
<bound method Point.distance of <classe.Point object at 0x000001F18395DAC0>>
>>> c.Point.distance
<function Point.distance at 0x000001F18396B5E0>

>>> c.p.__dict__
{'x': 1, 'y': 2}
>>> c.Point.__dict__
mappingproxy({'__module__': 'classe', 'z': 0, '__init__': <function Point.__init__ at 0x000001F18396B550>, 'distance': <function Point.distance at 0x000001F18396B5E0>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None})

尝试对p.distance()​和 p.distance​语句进行反编译可以得到以下字节码:

>>> dis.dis(compile('p.distance()', '', 'exec'))
  1           0 LOAD_NAME                0 (p)
              2 LOAD_METHOD              1 (distance)
              4 CALL_METHOD              0
              6 POP_TOP
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE

>>> dis.dis(compile('p.distance', '', 'exec'))
  1           0 LOAD_NAME                0 (p)
              2 LOAD_ATTR                1 (distance)
              4 POP_TOP
              6 LOAD_CONST               0 (None)
              8 RETURN_VALUE

类中的方法实际是一个非数据属性描述符:

>>> Point.distance
<function Point.distance at 0x10087a730>
>>> Point.distance.__get__(p)
<bound method Point.distance of <__main__.Point object at 0x100896630>>

这样一来,我们对 distance 进行属性查找得到的是一个 bound method ,它的底层结构如下:

image

bound method 底层是一个 PyMethodObject 结构体,定义于 Include/classobject.h 头文件中。该结构体除了一些公共头部,只有两个重要字段:

  • im_func ,函数对象;
  • im_self ,绑定的实例对象;
>>> bm = p.distance
>>> bm
<bound method Point.distance of <__main__.Point object at 0x100896630>>

>>> bm.__func__
<function Point.distance at 0x10087a730>
>>> bm.__func__ is Point.distance
True

>>> bm.__self__
<__main__.Point object at 0x100896630>
>>> bm.__self is p

因此下面的方式是等价的:

>>> bm()
2.23606797749979
>>> bm.__func__(bm.__self__)
2.23606797749979