简体   繁体   English

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

[英]Python: calling a function as a method of a class

Let's start with some code: 让我们从一些代码开始:

def func(*x):
    print('func:', x)


class ABC:
    def __init__(self, f):
        self.f1 = f

    def f2(*x):
        print('f2:', x)

Now we do some tests: 现在我们进行一些测试:

>>> 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>

Note that func is a normal function and we are making it a method f1 of the class. 注意func是一个普通函数,我们将其作为类的方法f1

We can see that f2 is getting the class instance as the first argument, but f1 and f3 are not, even though all functions are called as class methods. 我们可以看到f2将类实例作为第一个参数,但是f1f3不是,即使所有函数都称为类方法。 We can also see that if we call a normal function as a method of a class, Python does not make a bound method from it. 我们还可以看到,如果我们将普通函数作为类的方法来调用,Python不会从中创建绑定方法。

So why is f1 or f3 NOT getting a class instance passed to it even when we are calling it as a method of a class? 那么,即使我们将f1f3作为类的方法来调用,为什么f1f3没有传递给它呢? And also, how does Python know that we are calling an outer function as a method so that it should not pass an instance to it. 而且,Python如何知道我们将外部函数作为方法来调用,因此它不应将实例传递给它。

-- EDIT -- -编辑-

OK, so basically what I am doing wrong is that I am attaching the functions on the instance and NOT on the class object itself. 好的,所以基本上我做错的是我将函数附加在实例上,而不是附加在类对象本身上。 These functions therefore simply become instance attributes. 因此,这些功能仅成为实例属性。 We can check this with: 我们可以用以下方法检查:

>>> ABC.__dict__
... contents...
>>> a.__dict__
{'f1': <function func at 0xb74911ec>, 'f3': <function func at 0xb74911ec>}

Also note that this dict can not be assigned to: 另请注意,该字典不能分配给:

>>> ABC.__dict__['f4'] = func
TypeError: 'dict_proxy' object does not support item assignment

You kind of partially answered your own question inspecting the object. 您有点回答了自己检查对象的问题。 In Python, objects behave like namespaces, so the first attribute points to a function and the second points to a method. 在Python中,对象的行为类似于名称空间,因此第一个属性指向函数,第二个属性指向方法。

This is how you can add a method dynamically: 这是您可以动态添加方法的方式:

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)

Note that it will bind the method to your instance, not to the base class. 请注意,它将方法绑定到您的实例,而不是基类。 In order to monkeypatch the ABC class: 为了修补 ABC类:

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

Monkeypatching is usually frowned upon in the Python circles, despite being popular in other dynamic languages (notably in Ruby when the language was younger). 尽管在其他动态语言(尤其是在语言年轻的Ruby中很流行)中,Monkeypatching通常在Python界不受欢迎。

If you ever resort to this powerful yet dangerous technique, my advice is: 如果您使用这种强大而危险的技术,我的建议是:

  • never, ever override an existing class method. 永远不要覆盖现有的类方法。 just don't. 只是不。

That's because f1 and f3 are not class method they are just references to a global function defined in __main__ : 这是因为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>>

you can do something like this to make a global function a class method: 您可以执行以下操作使全局函数成为类方法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM