为什么python实例方法可以调用,但静态方法和类方法不可调用?
我做了以下事情:
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__
)。为什么会出现这种不对称,或者它的用途是什么?
我在学习python检查工具时遇到过这个问题。
评论的其他评论:
评论中链接的SO答案说静态/类方法是descriptors,它们是不可调用的。现在我很好奇,为什么描述符不可调用,因为描述符是具有特定属性的类(__get__
,__set__
,__del___
之一)。
答案 0 :(得分:1)
为什么描述符不能被调用?基本上是因为它们不需要被调用。并非每个描述符都代表一个可调用对象。
您正确地注意到,描述符协议由__get__
,__set__
和__del__
组成。请注意,没有__call__
,这是不能调用它的技术原因。实际的可调用对象是您的static_method.__get__(...)
的返回值。
从哲学的角度来看,让我们看一下课堂。 __dict__
的内容,或者在您的情况下为vars()
的结果,基本上是locals()
块的class
。如果您def
使用某个函数,该函数将作为普通函数转储。如果您使用装饰器,例如@staticmethod
,则等效于:
def _this_is_not_stored_anywhere():
pass
static_method = staticmethod(_this_is_not_stored_anywhere)
即static_method
被分配了staticmethod()
函数的返回值。
现在,函数对象实际上实现了装饰器协议-每个函数都具有一个__get__
方法。这是特殊的self
和绑定方法行为的来源。参见:
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
绑定到该实例,并使其作为第一个参数预先填充。
但是@classmethod
和@staticmethod
的全部意义在于,它们做了一些特殊的操作来避免默认的“绑定方法”行为!因此,它们无法返回普通函数。相反,它们返回带有自定义__get__
实现的描述符对象。
当然,您可以 在此描述符对象上放置一个__call__
方法,但是为什么呢?这是您实际上不需要的代码。您几乎永远无法触摸描述符对象本身。如果您这样做(使用与您的代码类似的代码),则仍需要对描述符进行特殊处理,因为通用描述符不必(必须像)可调用-属性也是描述符。因此,您不希望描述符协议中使用__call__
。因此,如果第三方“忘记”对您认为“可调用”的东西实施__call__
,则您的代码会错过它。
此外,对象是描述符,而不是函数。在其上放一个__call__
方法将掩盖其真正的本性:)我的意思是,它本身并不是错误,它只是……您永远不需要的东西。>
顺便说一句,在使用classmethod / staticmethod的情况下,您可以从其__func__
属性中获取原始函数。