[英]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.