# 从字节码看实例方法
有类定义和实例对象以及测试如下,很明显可以注意到所有方法都是定义在类的属性空间中的,并且当我们从实例和类的角度去尝试查看一个“实例方法”的本质是发现从实例的角度出发其不再是我们定义的函数对象,而是一个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 ,它的底层结构如下:
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