关于Python继承classmethod的一些问题

Some problems about Python inherited classmethod

我有这个代码:

from typing import Callable, Any


class Test(classmethod):
    def __init__(self, f: Callable[..., Any]):
        super().__init__(f)

    def __get__(self,*args,**kwargs):
        print(args)  # why out put is (None, <class '__main__.A'>) where form none  why no parameter 123
        # where was it called
        return super().__get__(*args,**kwargs)


class A:
    @Test
    def b(cls,v_b):
        print(cls,v_b)

A.b(123)

为什么输出是(None, <class '__main__.A'>)None 是从哪里来的,为什么不是 参数 123,这是我调用它的值?

方法 bA class 中检索时调用 __get__ 方法。它与 b.

的实际 调用 无关

为了说明这一点,将对 b 的访问与对 b 的实际调用分开:

print("Getting a reference to method A.b")
method = A.b
print("I have a reference to the method now. Let's call it.")
method()

这会产生以下输出:

Getting a reference to method A.b
(None, <class '__main__.A'>)
I have a reference to the method now. Let's call it.
<class '__main__.A'> 123

所以你看,__get__ 中的输出没有显示任何有关你调用 b 的参数的信息是正常的,因为你还没有进行调用。


输出None, <class '__main__.A'>符合Python documentation on __get__:

object.__get__(self, instance, owner=None)

Called to get the attribute of the owner class (class attribute access) or of an instance of that class (instance attribute access). The optional owner argument is the owner class, while instance is the instance that the attribute was accessed through, or None when the attribute is accessed through the owner.

在您的情况下,您使用它来访问 class (A) 的属性 (b) -- 不是A 的一个实例——这样就解释了 instance 参数是 Noneowner 参数是你的 class A.


使用 print(cls,v_b) 生成的第二个输出将为 cls 打印 <class '__main__.A'>,因为当您调用 class[ 时会发生这种情况=67=] 方法(相对于 实例 方法)。同样,来自 documentation:

When a class attribute reference (for class C, say) would yield a class method object, it is transformed into an instance method object whose __self__ attribute is C.

此处描述了您的情况,其中 A 是 class,因此第一个参数(您称为 cls)将获得值 A

您可以在同一个函数上应用多个装饰器,例如,

  • 第一个(和外部)装饰器可以是类方法
  • 第二个(做你的事情)可以定义一个包装器,你可以像往常一样接受你的参数
In [4]: def test_deco(func):
   ...:     def wrapper(cls, *args, **kwds):
   ...:         print("cls is", cls)
   ...:         print("That's where 123 should appear>>>", args, kwds)
   ...:         return func(cls, *args, **kwds)
   ...: 
   ...:     return wrapper
   ...: 
   ...: 
   ...: class A:
   ...:     @classmethod
   ...:     @test_deco
   ...:     def b(cls, v_b):
   ...:         print("That's where 123 will appear as well>>>", v_b)
   ...: 
   ...: 
   ...: A.b(123)
cls is <class '__main__.A'>
That's where 123 should appear>>> (123,) {}
That's where 123 will appear as well>>> 123

In [5]: 

It's too much trouble to use two at a time i want only use one like it

可以定义一个应用几个其他装饰器的装饰器:

def my_super_decorator_doing_everything_at_once(func):
    return classmethod(my_small_decorator_doing_almost_everything(func))

之所以可行,是因为装饰器表示法

@g
@f
def x(): ...

是一种可读的表达方式

def x(): ...
x = g(f(x))