让我们从一些代码开始:
def func(*x):
print('func:', x)
class ABC:
def __init__(self, f):
self.f1 = f
def f2(*x):
print('f2:', x)
现在我们做一些测试:
>>> a = ABC(func)
>>> a.f1(10)
func: (10,)
>>> a.f2(10)
f2: (<__main__.ABC object at 0xb75381cc>, 10)
>>> a.f3 = func
>>> a.f3(10)
func: (10,)
>>> a.f1
<function func at 0xb74911ec>
>>> a.f2
<bound method ABC.f2 of <__main__.ABC object at 0xb75381cc>>
>>> a.f3
<function func at 0xb74911ec>
请注意,func
是一个普通函数,我们将它作为该类的f1
方法。
我们可以看到f2
将类实例作为第一个参数,但f1
和f3
不是,即使所有函数都被调用为类方法。我们还可以看到,如果我们将普通函数称为类的方法,Python就不会从中创建绑定方法。
那么,为什么f1
或f3
没有将类实例传递给它,即使我们将它作为类的方法调用?而且,Python如何知道我们将外部函数作为方法调用,以便它不应该将实例传递给它。
- 编辑 -
好的,基本上我错误的是我在实例上附加函数而不是类对象本身。因此,这些函数只是成为实例属性。我们可以查看:
>>> ABC.__dict__
... contents...
>>> a.__dict__
{'f1': <function func at 0xb74911ec>, 'f3': <function func at 0xb74911ec>}
另请注意,此dict无法分配到:
>>> ABC.__dict__['f4'] = func
TypeError: 'dict_proxy' object does not support item assignment
答案 0 :(得分:8)
你有点部分回答了你自己检查对象的问题。在Python中,对象的行为类似于名称空间,因此第一个属性指向一个函数,第二个属性指向一个方法。
这是您动态添加方法的方法:
from types import MethodType
def func(*x):
print('func:', x)
class ABC:
def __init__(self, f):
self.f1 = MethodType(f, self, self.__class__)
def f2(*x):
print('f2:', x)
if __name__ == '__main__':
a = ABC(func)
print a.f1(10)
print a.f2(10)
a.f3 = MethodType(func, a, ABC)
print a.f3(10)
请注意,它会将方法绑定到您的实例,而不是绑定到基类。为了monkeypatch ABC班:
>>> ABC.f4 = MethodType(func, None, ABC)
>>> a.f4(1)
('func:', (<__main__.ABC instance at 0x02AA8AD0>, 1))
在Python圈子中,Monkeypatching通常不受欢迎,尽管在其他动态语言中很受欢迎(特别是在语言较年轻时使用Ruby)。
如果您采用这种强大但危险的技术,我的建议是:
答案 1 :(得分:3)
那是因为f1
和f3
不是类方法,它们只是对__main__
中定义的全局函数的引用:
In [5]: a.f1
Out[5]: <function __main__.func>
In [8]: a.f3
Out[8]: <function __main__.func>
In [9]: a.f2
Out[9]: <bound method ABC.f2 of <__main__.ABC instance at 0x8ac04ac>>
你可以做这样的事情,使全局函数成为一个类方法:
In [16]: class ABC:
def __init__(self,f):
ABC.f1=f
def f2(*x):
print('f2',x)
....:
In [17]: a=ABC(func)
In [18]: a.f1(10)
('func:', (<__main__.ABC instance at 0x8abb7ec>, 10))