Python中的类装饰器:实际用例


9

我正在寻找Python类装饰器的实用和非综合用例。到目前为止,对我而言唯一有意义的情况是在发布者-订阅者系统中注册一个类,例如插件或事件,例如:

@register
class MyPlugin(Plugin):
    pass

要么

@recieves_notifications
class Console:
    def print(self, text):
        ...

我一直在考虑的任何其他明智的情况都可以建立在继承,元类或修饰方法之上。您能否分享使用类装饰器的任何好(或坏!)示例?

谢谢!


对于初学者来说,装饰器在概念上比元类简单得多。编写装饰器要比元类简单得多,并且避免了可能不必弄清楚如何组合多个元类而不破坏任何内容。
阿蒙

@amon当经验丰富的开发人员使用元类时,通常意味着已经彻底考虑了其他选择,而元类到目前为止是解决问题的最佳方法。而且,显式比隐式好 -这可能就是为什么(例如)我们拥有ABCMeta而非@abstractclass类装饰器的原因。
Zaur Nasibov '16

2
我是一名Python程序员,老实说我没有必要。仅仅因为语言功能存在并不意味着您必须甚至应该使用它。设计师提出了各种疯狂的功能,这些功能后来看来是不必要的,甚至是有害的。实际上,您在问题中提到了其他两个类似的功能:)
gardenhead

1
不是没有我不能没有的东西,但是我发现有用的是使用类装饰器来记录该类所有方法的所有输入和输出。
bgusach '16

Answers:


8

编写具有@unittest.skipIf和的单元测试时,@unittest.skipUnless可以在单个方法或整个unittest.TestCase类定义中使用。这使得编写针对平台特定问题的测试变得非常容易。

来自的例子 asyncio/test_selectors

# Some platforms don't define the select.kqueue object for these tests.
# On those platforms, this entire grouping of tests will be skipped.

@unittest.skipUnless(hasattr(selectors, 'KqueueSelector'),
                 "Test needs selectors.KqueueSelector)")
class KqueueSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixIn):
...

2

被明确创建的类的装饰做一些事情,已经通过元类更愉快的表达,从PEP

激发用例的目的是使某些构造更易于表达,并且更少依赖CPython解释器的实现细节。尽管可以使用元类表达类装饰器类的功能,但结果通常令人不快,实现起来也很脆弱。另外,元类是继承的,而类装饰器则不是,这使得元类不适合类装饰器的某些特定于类的使用。诸如Zope之类的大型Python项目正在经历这些疯狂的扭曲,从而实现了诸如类装饰器之类的事实,这一事实赢得了BDFL的青睐。


感谢您引用PEP-3129。但是,问题不在于类装饰器为什么存在,Guido如何看待它们,以及Zope或IronPython在它们出现之前如何使用它们。问题是关于今天的实际使用情况,即2016
。– Zaur Nasibov

1
是的,关键是最初的问题提出了元类,可以用来实现类装饰器的功能,但是装饰器的全部要点是它们比元类更易于使用和显而易见。因此,对于该部分问题的答案是“如果您在装饰器或元类之间陷入困境,则应使用装饰器,因为它们易于他人理解。”
quodlibetor

您将留下更多可以回答的问题。“ ...装饰器的全部要点是它们比元类更易于使用且更明显”-在哪种情况下?然后,“ ...您应该使用装饰器,因为它们更易于他人理解”,真的吗?长期以来,我沉迷于Python,我见过大量的元类,有些丑陋,有些漂亮。但是根本没有发现任何类装饰器。
Zaur Nasibov

1

这个堆栈溢出问题:

def singleton(class_):
  instances = {}
  def getinstance(*args, **kwargs):
    if class_ not in instances:
        instances[class_] = class_(*args, **kwargs)
    return instances[class_]
  return getinstance

@singleton
class MyClass(BaseClass):
  pass

是的,但是在同一个SO问题中却给出了相反的结论:元类是一种更好的方法。
Zaur Nasibov '16

1

寻找可以在类上使用装饰器完成的用例是徒劳的- 在类上可以使用装饰器完成的任何事情都可以通过元类完成。甚至您的注册示例。为了证明这一点,这是一个应用装饰器的元类:

Python 2:

def register(target):
    print 'Registring', target
    return target


class ApplyDecorator(type):
    def __new__(mcs, name, bases, attrs):
        decorator = attrs.pop('_decorator')
        cls = type(name, bases, attrs)
        return decorator(cls)

    def __init__(cls, name, bases, attrs, decorator=None):
        super().__init__(name, bases, attrs)


class Foo:
    __metaclass__ = ApplyDecorator
    _decorator = register

Python 3:

def register(target):
    print('Registring', target)
    return target


class ApplyDecorator(type):
    def __new__(mcs, name, bases, attrs, decorator):
        cls = type(name, bases, attrs)
        return decorator(cls)

    def __init__(cls, name, bases, attrs):
        super().__init__(name, bases, attrs)


class Foo(metaclass=ApplyDecorator, decorator=register):
    pass

1
正如您所说-装饰器可以完成任何操作。问题是,应该通过类修饰器而不是元类来处理哪些情况。
Zaur Nasibov '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.