简体   繁体   English

Class 变量引用 function 更改为实例方法

[英]Class variable reference to function changes to instancemethod

I'm trying to call an external function via a class variable.我正在尝试通过 class 变量调用外部 function 。 The following is a simplification of my real code:以下是我真实代码的简化:

def func(arg):
    print(arg)

class MyClass(object):
    func_ref = None

    @classmethod
    def setUpClass(cls):
        #MyClass.func_ref = func
        cls.func_ref = func

    @staticmethod
    def func_override(arg):
        print("override printing arg...")
        MyClass.func_ref(arg)

if __name__ == "__main__":
    print(type(func))
    print(type(MyClass.func_ref))
    MyClass.setUpClass()
    print(type(MyClass.func_ref))
    MyClass.func_override("hello!")

The above code produces the following output:上面的代码产生以下 output:

[~]$ python tmp.py
<type 'function'>
<type 'NoneType'>
<type 'instancemethod'>
override printing arg...
Traceback (most recent call last):
  File "tmp.py", line 20, in <module>
    MyClass.func_override("hello!")
TypeError: func_override() takes exactly 2 arguments (1 given)

The situation seems to be unchanged if I use MyClass in place of cls within the classmethod setUpClass() .如果我在类方法setUpClass()中使用MyClass代替cls ,情况似乎没有改变。

I would expect the type of MyClass.func_ref to be function after the assignment in setUpClass() which explains the TypeError I get when I try to call it.我希望MyClass.func_ref的类型在setUpClass()中的分配之后是function这解释了我尝试调用它时遇到的TypeError Why is the type of func_ref being changed to instancemethod when the value I assigned to it is of type function ?当我分配给它的值是function类型时,为什么将func_ref的类型更改为instancemethod

This only seems to be an issue in Python 2. Python 3 behaves as I would expect.这似乎只是 Python 2 中的一个问题。 Python 3 的行为与我预期的一样。

How do I get calls to the static method MyClass.func_override() to call func() ?如何调用 static 方法MyClass.func_override()来调用func()

UPDATE更新

I was able to get the above to work by applying the following patch:通过应用以下补丁,我能够使上述工作:

@@ -14,7 +14,7 @@ class MyClass(object):
     def func_override(arg):
         print("override printing arg...")
         func(arg)
-        MyClass.func_ref.__func__(arg)
+        MyClass.func_ref(arg)

 if __name__ == "__main__":
     print(type(func))

While the above works, its not at all clear to me why I needed to do this.虽然上述方法有效,但我完全不清楚为什么我需要这样做。 I still don't understand why the type of func_ref ends up an instancemethod when I assigned to it a value of type function .我仍然不明白为什么 func_ref 的类型在我为其分配func_ref类型的值时最终会成为一个instancemethod function

Just put the function through a staticmethod as follows:只需通过staticmethod方法将function放入如下:

    @classmethod
    def setUpClass(cls):
        #MyClass.func_ref = func
        cls.func_ref = staticmethod(func)

There's no need to play with @-based decorators in this case as you want to modify how the method is bound to MyClass, not the general definition of func .在这种情况下,无需使用基于 @ 的装饰器,因为您想修改方法绑定到 MyClass 的方式,而不是func的一般定义。

Why is this necessary?为什么这是必要的? Because, when you assign a method to class, Python assumes you'll want to refer to an instance (via self ) or the class (via cls ).因为,当您将方法分配给 class 时,Python 假定您想要引用一个实例(通过self )或 class (通过cls )。 self , unlike this in JS, is only a naming convention, so when it sees arg it assumes it got an instance, but you passed a string in your call. self与 JS 中的this不同,它只是一个命名约定,所以当它看到arg时,它假定它有一个实例,但你在调用中传递了一个字符串。

So, as as Python cares, you might have as well have written def func(self): .因此,正如 Python 所关心的那样,您也可以编写def func(self): Which is why the message says unbound method func() must be called with MyClass instance as first argument .这就是为什么消息说unbound method func() must be called with MyClass instance as first argument的原因。

staticmethod means, "please leave this alone and don't assume an instance or a class in the first variable". staticmethod的意思是,“请不要管它,不要在第一个变量中假设实例或 class”。

You can even dispense with the setUpClass entirely :你甚至可以完全省去setUpClass entirely

class MyClass(object):
    func_ref = staticmethod(func)

BTW: In 2021, 16 months past EOL, Python 2.7 has all the subtle fagrance of moldy gym socks.顺便说一句:在 2021 年,即 EOL 过去 16 个月后,Python 2.7 具有发霉运动袜的所有微妙气味。 Except less safe, virologically-speaking.除了不太安全,从病毒学上讲。

When func_ref is called, it's expecting a self argument, just like any other normal (instance) class method (see this question and answers for discussions why).func_ref时,它需要一个self参数,就像任何其他正常(实例)class 方法一样(请参阅此问题和答案以了解原因)。 You can either add a self argument to func or make func a static method:您可以将self参数添加到func或使func成为 static 方法:

@staticmethod
def func(arg):
    print(arg)

>>> MyClass.setUpClass()
>>> MyClass.func_override("hello!")
override printing arg...
hello!

Note that in either case func is now not normally callable as a regular function:请注意,无论哪种情况, func现在通常都不能作为常规 function 调用:

>>> func('what does this do?')
TypeError: 'staticmethod' object is not callable

If you need func to be usable as a regular function, you can wrap it with another, qualifying function and use the wrapper in MyClass :如果您需要func作为常规 function 使用,您可以用另一个符合条件的 function 包装它并在MyClass中使用包装器:

def func(arg):
    print(arg)

@staticmethod
def func_wrapper(arg):
    func(arg)

class MyClass(object):
    @classmethod
    def setUpClass(cls):
        cls.func_ref = func_wrapper  # use wrapper function

>>> MyClass.setUpClass()
>>> MyClass.func_override("success!")
override printing arg...
success!

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

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