为什么python静态/类方法不可调用?

时间:2016-07-14 03:27:37

标签: python oop methods

为什么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___之一)。

1 个答案:

答案 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__属性中获取原始函数。