1

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

问题:但是,用Animal.classy修饰的方法@classmethod给出type了它的类名,而用Animal.static修饰的方法@staticmethod无法检索到它的类。

@wrapper装饰器函数是否可以Animal获取Animal.classy()and的类名Animal.static()

预期产出

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
4

1 回答 1

4

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

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

接下来method调用Animal. 您的包装器检查是否instance已设置,因为方法获取实例参数,所以它打印出类instance的名称和方法的名称。

接下来你叫classyAnimal。您的包装器检查是否instance已设置,因为类方法获取实例参数,所以它打印出类instance的名称和类方法的名称。但是在这种情况下instance是定义该方法的类,因此当您这样做时,instance.__class__它会检索您的类的元Animal类,即type. 所以在这种情况下你真正想要的是添加一个检查if isinstance(instance, type),在这种情况下你想要做的是print(f"{instance.__name__}.{wrapped.__name__}")因为在这种情况下 instance 是你的类。

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

因此,总而言之,回答您的确切问题。是否可以获得类名classy?是的。是否可以获得类名static?不是我知道的。

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

特别是它提供了示例:

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)

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

希望这是有道理的。

于 2019-12-19T05:13:24.873 回答