【问题标题】:Class variable reference to function changes to instancemethod类变量引用函数更改为instancemethod
【发布时间】:2021-04-28 16:08:18
【问题描述】:

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

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)

如果我在类方法setUpClass() 中使用MyClass 代替cls,情况似乎没有改变。

我希望MyClass.func_ref 的类型在分配setUpClass() 之后是function,这解释了我尝试调用它时得到的TypeError。为什么我分配给func_ref的值是function类型时,它的类型会变成instancemethod

这似乎只是 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

【问题讨论】:

    标签: python python-2.7 static-methods class-method


    【解决方案1】:

    只需将函数通过staticmethod 如下:

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

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

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

    因此,正如 Python 所关心的那样,您不妨编写 def func(self):。这就是为什么消息说unbound method func() must be called with MyClass ?instance? as first argument

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

    你甚至可以不用setUpClass entirely

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

    顺便说一句:在 2021 年,即 EOL 之后的 16 个月,Python 2.7 具有发霉的运动袜的所有微妙气味。除了不太安全,从病毒学角度讲。

    【讨论】:

    • 谢谢! “Python 假设你会想要……”正是我想要的。我会把 2.7 的香味加到热车里的发霉运动袜上。但即使有恶臭,2.7 对于一些大型且昂贵的系统仍然是关键任务。再次感谢!
    【解决方案2】:

    func_ref 被调用时,它需要一个self 参数,就像任何其他普通(实例)类方法一样(有关原因的讨论,请参见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!
    

    【讨论】:

    • 谢谢!我知道func_ref 现在期待self 参数,但为什么呢?我给它分配了一个“正常”函数的值——而不是一个方法。我希望func 保持与类无关的“正常”函数(与任何类型的方法相反,静态或其他)
    • @awm129 已更新以解决您的评论
    • 再次感谢。包装器可以工作,但我真的想通过我的类变量调用func。我想出了一些可行的方法,但我完全不清楚它为什么有效。
    猜你喜欢
    • 1970-01-01
    • 2019-03-19
    • 1970-01-01
    • 2011-06-24
    • 2017-03-16
    • 1970-01-01
    • 1970-01-01
    • 2017-02-01
    • 1970-01-01
    相关资源
    最近更新 更多