为什么python static/class方法不可调用? [英] Why are python static/class method not callable?
问题描述
为什么python实例方法可以调用,但静态方法和类方法不能调用?
Why are python instance methods callable, but static methods and class methods not callable?
我做了以下事情:
class Test():
class_var = 42
@classmethod
def class_method(cls):
pass
@staticmethod
def static_method():
pass
def instance_method(self):
pass
for attr, val in vars(Test).items():
if not attr.startswith("__"):
print (attr, "is %s callable" % ("" if callable(val) else "NOT"))
结果是:
static_method is NOT callable
instance_method is callable
class_method is NOT callable
class_var is NOT callable
从技术上讲,这可能是因为实例方法对象可能具有以特定方式(可能是__call__
)设置的特定属性(未设置).为什么会有这种不对称性,或者它有什么作用?
Technically this may be because instance method object might have a particular attribute (not) set in a particular way (possibly __call__
). Why such asymmetry, or what purpose does it serve?
我在学习python检查工具时碰到了这一点.
I came across this while learning python inspection tools.
评论中的其他评论:
注释中链接的SO答案表示静态/类方法为描述符,不可调用.现在我很好奇,为什么描述符不能被调用,因为描述符是具有特定属性(__get__
,__set__
,__del___
之一)的类.
The SO answer linked in the comments says that the static/class methods are descriptors , which are not callable. Now I am curious, why are descriptors made not callable, since descriptors are class with particular attributes (one of __get__
, __set__
, __del___
) defined.
推荐答案
为什么描述符不能被调用?基本上是因为它们不需要被调用.并非每个描述符都代表一个可调用对象.
Why are descriptors not callable? Basically because they don't need to be. Not every descriptor represents a callable either.
您正确地注意到,描述符协议由__get__
,__set__
和__del__
组成.注意没有__call__
,这就是为什么它不能被调用的技术原因.实际的可调用对象是您的static_method.__get__(...)
的返回值.
As you correctly note, the descriptor protocol consists of __get__
, __set__
and __del__
. Note no __call__
, that's the technical reason why it's not callable. The actual callable is the return value of your static_method.__get__(...)
.
从哲学的角度来看,让我们看一看课. __dict__
的内容,或者在您的情况下为vars()
的结果,基本上是class
块的locals()
.如果def
使用一个函数,它将作为普通函数转储.如果使用装饰器(例如@staticmethod
),则它等效于:
As for the philosophical reason, let's look at the class. The contents of the __dict__
, or in your case results of vars()
, are basically locals()
of the class
block. If you def
ine a function, it gets dumped as a plain function. If you use a decorator, such as @staticmethod
, it's equivalent to something like:
def _this_is_not_stored_anywhere():
pass
static_method = staticmethod(_this_is_not_stored_anywhere)
即static_method
被分配了staticmethod()
函数的返回值.
I.e., static_method
is assigned a return value of the staticmethod()
function.
现在,函数对象实际上实现了装饰器协议-每个函数都具有一个__get__
方法.这是特殊的self
和绑定方法行为的来源.参见:
Now, function objects actually implement the decorator protocol - every function has a __get__
method on it. This is where the special self
and the bound-method behavior comes from. See:
def xyz(what):
print(what)
repr(xyz) # '<function xyz at 0x7f8f924bdea0>'
repr(xyz.__get__("hello")) # "<bound method str.xyz of 'hello'>"
xyz.__get__("hello")() # "hello"
由于该类如何调用__get__
,因此您的test.instance_method
绑定到该实例,并使其作为第一个参数预先填充.
Because of how the class calls __get__
, your test.instance_method
binds to the instance and gets it pre-filled as it first argument.
但是@classmethod
和@staticmethod
的全部要点是,它们做了一些特殊的操作来避免默认的绑定方法"行为!所以他们不能返回一个普通的函数.相反,它们返回带有自定义__get__
实现的描述符对象.
But the whole point of @classmethod
and @staticmethod
is that they do something special to avoid the default "bound method" behavior! So they can't return a plain function. Instead they return a descriptor object with a custom __get__
implementation.
当然,您可以在此描述符对象上放置一个__call__
方法,但是为什么呢?这是您实际上不需要的代码.您几乎永远无法触摸描述符对象本身.如果您这样做(使用与您的代码类似的代码),则仍需要对描述符进行特殊处理,因为通用描述符不必(必须像)可调用-属性也是描述符.因此,您不需要描述符协议中的__call__
.因此,如果第三方忘记"对您认为可调用"的东西实施__call__
,则您的代码将丢失它.
Of course, you could put a __call__
method on this descriptor object, but why? It's code that you don't need in practice; you can almost never touch the descriptor object itself. If you do (in code similar to yours), you still need special handling for descriptors, because a general descriptor doesn't have to be(have like a) callable - properties are descriptors too. So you don't want __call__
in the descriptor protocol. So if a third party "forgets" to implement __call__
on something you consider a "callable", your code will miss it.
此外,对象是描述符,而不是函数.在其上放一个__call__
方法将掩盖其真正的本性:)我的意思是,这本身并不是错误,它只是……您根本不需要的东西.
Also, the object is a descriptor, not a function. Putting a __call__
method on it would be masking its true nature :) I mean, it's not wrong per se, it's just ... something that you should never need for anything.
顺便说一句,对于classmethod/staticmethod而言,您可以从其__func__
属性中获取原始功能.
BTW, in case of classmethod/staticmethod, you can get back the original function from their __func__
attribute.
这篇关于为什么python static/class方法不可调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!