【问题标题】:@decorator get Class.__name__ of @staticmethod and @classmethod@decorator 获取 @staticmethod 和 @classmethod 的 Class.__name__
【发布时间】:2020-04-11 07:39:56
【问题描述】:

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

问题: 但是,用@classmethod 装饰的方法Animal.classytype 作为其类名,而用@staticmethod 装饰的方法Animal.static 无法检索其类。

@wrapper装饰器函数是否可以获取Animal.classy()Animal.static()的类名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

【问题讨论】:

  • 为什么你在类而不是实例上调用classy和static,就像你对方法所做的那样?尝试做Animal().classy()Animal().static()
  • 我不确定您将如何检测您正在寻找的区别。 method 正在获取 Animal 的实例作为参数,但 classy 只是获取 type 的实例。 static 当然,没有得到任何争论。这三个对象都不知道它们被用作Animal 的类属性,因此通常在wrapper 的主体中没有可用的信息。
  • 也就是说,Animal.method 引用了一个函数对象,但该函数对象不知道它是作为Animal 类属性引用的。绑定到Animal.classyAnimal.static 的函数也是如此。
  • @Pynchia 无论您使用Animal.classy() 还是Animal().classy()classy 仍然只是获得对Animaltype 的实例)的引用作为参数。
  • @chepner 我认为Animal.method 确实知道类名。因为它知道它所属的实例,它可以通过instance.__class__ 来获取实例的类,因此instance.__class__.__name__ 来获取类名。我的例子能够证明这一点。

标签: python python-3.x decorator wrapt


【解决方案1】:

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

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

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

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

最后你在你的类Animal 上调用静态方法static。当您声明一个静态方法时,它的行为就像一个普通函数。因此,您的包装器检查是否设置了 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)

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

希望这是有道理的。

【讨论】:

  • 感谢您在 Stackoverflow 上发表精彩的第一篇文章!欢迎!
  • Np,很高兴我能做出贡献。潜伏了好久哈哈
猜你喜欢
  • 2012-08-24
  • 2021-08-14
  • 2019-04-28
  • 2019-08-14
  • 1970-01-01
  • 2011-10-04
  • 1970-01-01
  • 2012-07-30
  • 2018-04-15
相关资源
最近更新 更多