4

让我们从一些代码开始:

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 不会从中创建绑定方法。

那么,即使我们将其作为类的方法调用,为什么还要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
4

2 回答 2

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)

请注意,它将方法绑定到您的实例,而不是基类。为了对ABC 类进行猴子补丁:

>>> ABC.f4 = MethodType(func, None, ABC)
>>> a.f4(1)
('func:', (<__main__.ABC instance at 0x02AA8AD0>, 1))

Monkeypatching 在 Python 圈子中通常不受欢迎,尽管它在其他动态语言中很流行(尤其是在 Ruby 语言更年轻的时候)。

如果你曾经求助于这种强大而危险的技术,我的建议是:

  • 永远,永远覆盖现有的类方法。只是不要。
于 2012-11-23T07:09:29.547 回答
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))
于 2012-11-23T07:03:25.780 回答