Python:将函数作为类的方法调用

时间:2012-11-23 06:55:22

标签: python

让我们从一些代码开始:

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将类实例作为第一个参数,但f1f3不是,即使所有函数都被调用为类方法。我们还可以看到,如果我们将普通函数称为类的方法,Python就不会从中创建绑定方法。

那么,为什么f1f3没有将类实例传递给它,即使我们将它作为类的方法调用?而且,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

2 个答案:

答案 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)

那是因为f1f3不是类方法,它们只是对__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))