@decorator 获得@staticmethod 和@classmethod 的Class.__name__

@decorator get Class.__name__ of @staticmethod and @classmethod

装饰器 @wrapper 正在使用 wrapt 库访问包装函数的 class 以获取 class 的名称。在 Animal.method()foo() 上使用它按预期工作。

问题: 但是,用 @classmethod 修饰的方法 Animal.classy 给出 type 作为其 class 名称,并且用 @staticmethod 修饰的方法 Animal.static 无法检索其 class.

@wrapper 装饰器函数是否可以为 Animal.classy()Animal.static() 获取 class 名称 Animal

预期输出

foo
Animal.method
Animal.classy
Animal.static

获得输出

foo
Animal.method
type.classy
static

重现代码

import wrapt
import time

@wrapt.decorator
def wrapper(wrapped, instance, args, kw):
    if instance is not None:
        print(f'\n{instance.__class__.__name__}.{wrapped.__name__}')
    else:
        print('\n' + wrapped.__name__)
    result = wrapped(*args, **kw)
    return result

@wrapper
def foo():
    time.sleep(0.1)

class Animal:
    @wrapper
    def method(self):
        time.sleep(0.1)

    @wrapper
    @classmethod
    def classy(cls):
        time.sleep(0.1)

    @wrapper
    @staticmethod
    def static():
        time.sleep(0.1)

if __name__ == '__main__':
    foo()                   # foo
    Animal().method()       # Animal.method
    Animal.classy()         # type.classy
    Animal.static()         # static

您遇到的问题是 arg instance 是任何方法的第一个参数的值。因此,对于常规方法,这将是 self 值,而对于 class 方法,它将是 cls 值。对于静态方法,您没有 first/instance 参数,因此它的行为就好像它是一个函数并且 instanceNone.

那么让我们来看看你的代码。你调用 foo,你的包装器检查是否设置了 instance,它不是因为函数没有获得实例参数,所以它打印出函数的名称。

接下来在 Animal 的实例上调用 method。您的包装器检查是否设置了 instance,这是因为方法获得了一个实例参数,所以它打印出 instance 的 class 的名称和方法的名称。

接下来您在 class Animal 上调用 classy。您的包装器检查是否设置了 instance,这是因为 class 方法获得了一个实例参数,所以它打印出 instance 的 class 的名称和 instance 的名称class 方法。但是,在这种情况下,instance 是定义该方法的 class,因此当您执行 instance.__class__ 时,它会为您的 Animal [=63] 检索元 class =] 即 type。所以在这种情况下你真正想要的是添加一个检查 if isinstance(instance, type) 并且在这种情况下你想要做 print(f"{instance.__name__}.{wrapped.__name__}") 因为实例是你的 class 在这种情况下。

最后,您在 class Animal 上调用了静态方法 static。当您声明一个静态方法时,它的行为就像一个普通函数。所以你的包装器检查 instance 是否被设置并发现它没有,因为函数没有获得实例参数,所以继续并打印函数的名称。我不知道检测静态方法的标准方法。

所以总结一下回答你的确切问题。是否可以为 classy 获取 class 名称?是的。是否可以为 static 获取 class 名称?不是我知道的。

有关实现可应用于不同上下文的装饰器的更多信息,请参阅:

它特别提供了示例:

import inspect

@wrapt.decorator
def universal(wrapped, instance, args, kwargs):
    if instance is None:
        if inspect.isclass(wrapped):
            # Decorator was applied to a class.
            return wrapped(*args, **kwargs)
        else:
            # Decorator was applied to a function or staticmethod.
            return wrapped(*args, **kwargs)
    else:
        if inspect.isclass(instance):
            # Decorator was applied to a classmethod.
            return wrapped(*args, **kwargs)
        else:
            # Decorator was applied to an instancemethod.
            return wrapped(*args, **kwargs)

这可能有助于理解在每种情况下需要采取哪些步骤来计算名称。

希望这是有道理的。