为什么python静态/类方法不可调用? [英] Why are python static/class method not callable?

查看:51
本文介绍了为什么python静态/类方法不可调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么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() 的结果,基本上是 classlocals()代码>块.如果你define 一个函数,它会被转储为一个普通函数.如果你使用装饰器,比如 @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 define 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 descriptor 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静态/类方法不可调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆