Class 对函数的变量引用更改为 instancemethod

Class variable reference to function changes to instancemethod

我正在尝试通过 class 变量调用外部函数。以下是我的真实代码的简化:

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

以上代码产生以下输出:

[~]$ 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)

如果我在 class 方法 setUpClass().

中使用 MyClass 代替 cls,情况似乎没有改变

我希望 MyClass.func_ref 的类型在 setUpClass() 中的赋值后为 function,这解释了我在尝试调用它时得到的 TypeError。为什么 func_ref 的类型被更改为 instancemethod 而我分配给它的值是 function 类型?

这似乎只是 Python 2 中的一个问题。Python 3 的行为符合我的预期。

如何调用静态方法 MyClass.func_override() 来调用 func()

更新

通过应用以下补丁,我能够使上述功能正常工作:

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

虽然上述方法有效,但我完全不清楚为什么我需要这样做。我仍然不明白为什么 func_ref 的类型在我为它分配 function.

类型的值时以 instancemethod 结束

func_ref 被调用时,它需要一个 self 参数,就像任何其他普通(实例)class 方法一样(参见 this question and answers 讨论原因)。您可以将 self 参数添加到 func 或使 func 成为静态方法:

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

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

请注意,在任何一种情况下,func 现在通常都不能作为常规函数调用:

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

如果您需要 func 作为常规函数使用,您可以用另一个符合条件的函数包装它,并在 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!

只需将函数通过一个staticmethod如下:

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

在这种情况下不需要使用基于@的装饰器,因为您想修改方法绑定到 MyClass 的方式,而不是 func 的一般定义。

为什么这是必要的?因为,当您将方法分配给 class 时,Python 假定您将要引用一个实例(通过 self)或 class(通过 cls). self,与 JS 中的 this 不同,它只是一个命名约定,所以当它看到 arg 时,它假定它有一个实例,但你在调用中传递了一个字符串。

因此,正如 Python 所关心的那样,您也可以写 def func(self):。这就是消息显示 unbound method func() must be called with MyClass instance as first argument.

的原因

staticmethod 的意思是,“请别管它,不要在第一个变量中假设一个实例或 class”。

你甚至可以省去 setUpClass entirely:

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

顺便说一句:在 2021 年,也就是停产后 16 个月,Python 2.7 散发着发霉运动袜的所有微妙气味。除了不太安全,从病毒学角度来说。