在学习 Python 时,有没有形成一种观念,那就是 class 内的函数和外面的函数不一样?
通常来说,类中的函数叫作“方法”(method),它第一个参数会写成 self,并且调用时只需要传入后面的参数,不需要传入 self.
更进阶一点时,会遇到 @classmethod 和 @staticmethod 修饰器,经过了这些修饰器的类函数,第一个参数含义不同或者没有特殊含义。
但这究竟是怎么一回事?
尝试下列代码:
class MyClass:
def myFunc(self):
pass
obj = MyClass()
print(obj.myFunc)
print(obj.myFunc.__func__)
print(MyClass.myFunc)
print(obj.myFunc.__func__ is MyClass.myFunc)python会发现 obj.myFunc 是一个 bound method 对象,而其余两个都是 function 对象,并且其实是同一个 function 对象。
初见端倪?别急
稍微改动代码:
class MyClass:
def myFunc(self = "no self passed in"):
print(self)
obj = MyClass()
print(obj)
obj.myFunc()
obj.myFunc.__func__()
MyClass.myFunc()python会发现 MyClass.myFunc 得到的函数没有自动传入 self 参数,和普通的函数无异;而 obj.myFunc 得到的函数传入了 self 参数。
再做一个实验:
class MyClass:
pass
def normalFunc(arg1 = "no arg1 passed in"):
print(arg1)
MyClass.myFunc = normalFunc
obj = MyClass()
obj.myFunc()
obj.myFunc.__func__()
MyClass.myFunc()
normalFunc()python外部定义的普通函数,只要绑定到了类上,就能复刻类函数的行为?! 而且 self 参数也完全可以不叫 self.
这说明类函数的行为并不是由其定义位置决定的,只是由于它绑定在了类上,并且从类的实例上调用了。
那么,为什么从类实例上调用类函数,就会自动传入 self 作为第一个参数呢?
试试:
class MyClass:
pass
def normalFunc(arg1 = "no arg1 passed in"):
print(arg1)
MyClass.myFunc = normalFunc
obj = MyClass()
print(obj.myFunc)
print(normalFunc.__get__(obj, MyClass))
obj.myFunc()
normalFunc.__get__(obj, MyClass)()python发现 normalFunc.__get__(obj, MyClass) 也可以得到 bound method 对象,并且效果和 obj.myFunc 是一样的,即 self 参数传入了 obj.
终于可以揭开谜底了。实际上,Python 在你每次写下 obj.myFunc 时,都会查询 MyClass 里面是否存在名为 myFunc 的 function 对象,如果存在,就会返回 MyClass.myFunc.__get__(obj, MyClass).
至于为什么要这样做,以及这背后的底层机制,请见 Descriptor.
请注意,每次运行 MyClass.myFunc.__get__(obj, MyClass) 都会动态生成 bound method 对象,每次生成的对象都是不同的,这也解释了这个诡异现象:
class MyClass:
def myFunc(self):
pass
obj = MyClass()
print(obj.myFunc is obj.myFunc)pythonFalse