装饰者与继承

时间:2011-06-20 02:59:22

标签: python inheritance decorator

如何在可能的情况下决定使用装饰器和继承?

例如,this problem有两种解决方案。

我对Python特别感兴趣。

5 个答案:

答案 0 :(得分:19)

...装饰:

    如果您要做的是“包装”,则应使用
  • ....包装包括取东西,修改(或用某些东西注册),和/或返回一个行为“几乎完全”的代理对象。
  • ...可以应用类似mixin的行为,只要你不创建大量的代理对象。
  • ...有一个隐含的“堆叠”抽象:

e.g。

@decoA
@decoB
@decoC
def myFunc(...): ...
    ...

相当于:

def myFunc(...): ...
    ...
myFunc = decoA(decoB(decoC(myFunc)))  #note the *ordering*

多重继承......:

  • ...最适合在课程中添加方法;你不能用它来轻松地装饰功能。在这种情况下,如果您需要的是一组“鸭子式”额外方法,它可用于实现类似mixin的行为。
  • ...如果您的问题与它不匹配,可能有点笨拙,超类构造函数等问题。例如,除非明确调用,否则不会调用子类__init__方法(通过方法 - 解决方案协议)!

总而言之,如果他们没有返回代理对象,我会使用装饰器进行类似mixin的行为。一些示例将包括任何返回原始函数的装饰器,稍微修改(或在某处注册或将其添加到某个集合之后)。

你经常会找到装饰器的东西(比如memoization)也是很好的候选者,但如果他们返回代理对象,应该适度使用;它们的应用顺序很重要。并且太多的装饰器彼此之间以不打算使用它们的方式使用它们。

如果它是“经典继承问题”,或者如果我对mixin行为所需要的只是方法,我会考虑使用继承。经典的继承问题是您可以在任何可以使用父级的地方使用子项。

一般来说,我尝试编写不需要增强任意内容的代码。

答案 1 :(得分:3)

您引用的问题不是在装饰器和类之间进行决定。 使用装饰器,但您可以选择使用:

  • 一个装饰器,它返回一个类
  • 一个装饰器,它返回一个函数

装饰器只是“包装”模式的一个奇特名称,即用其他东西替换某些东西。实现取决于您(类或函数)。

在决定他们之间时,这完全取决于个人偏好。你可以做一切与另一个一起做的事情。

  • 如果装饰一个函数,你可能更喜欢返回代理函数的装饰器
  • 如果装饰一个类,你可能更喜欢返回代理类的装饰器

(为什么这是一个好主意?可能有假设装饰函数仍然是一个函数,而装饰类仍然是一个类。)

在这两种情况下,更好的方法是使用装饰器,它只返回原始的,以某种方式修改。

修改:在更好地理解您的问题后,我在Python functools.wraps equivalent for classes

发布了另一个解决方案

答案 2 :(得分:1)

如果两者都是等价的,我更喜欢装饰器,因为你可以为许多类使用相同的装饰器,而继承仅适用于一个特定的类。

答案 3 :(得分:1)

就个人而言,我会考虑代码重用。装饰器有时比继承更灵活。

我们以缓存为例。如果要将缓存工具添加到系统中的两个类:A和B,继承,您可能最终会使用ACached和BCached。通过覆盖这些类中的一些方法,您可能会复制相同缓存逻辑的大量代码。但是如果你在这种情况下使用装饰器,你只需要定义一个装饰器来装饰这两个类。

因此,在决定使用哪一个时,您可能首先要检查扩展功能是否仅特定于此类,或者是否可以在系统的其他部分中重用相同的扩展功能。如果它不能被重用,那么继承应该可以完成这项工作。否则,你可以考虑使用装饰器。

答案 4 :(得分:1)

其他答案都很好,但我想给出一个简短的利弊清单。

mixins的主要优点是可以在运行时使用isinstance来检查类型,并且可以使用MyPy之类的linter来检查它。像所有继承一样,当您具有 is-a 关系时,应使用它。例如,dataclass可能应该是mixin,以便公开特定于数据类的自省变量,例如数据类字段的列表。

当您没有 is-a 关系时,应该首选

装饰器。例如,一个装饰器从另一个类传播文档,或在某个集合中注册一个类。

装饰通常仅影响其装饰的类,而不影响从基类继承的类:

@decorator
class A:
    ...  # Can be affected by the decorator.

class B(A):
    ...  # Not affected by the decorator in most cases.

现在Python具有__init_subclass__,装饰器可以做的所有事情都可以用mixins完成,并且它们通常会影响子子类:

class A(Mixin):
    ... # Is affected by Mixin.__init_subclass__.

class B(A):
    ... # Is affected by Mixin.__init_subclass__.

总而言之,在决定混搭和装饰之间应该问的问题是:

  • 是否存在模式?
    • 您会打电话给isinstance吗?
    • 您会在类型注释中使用mixin吗?
  • 您是否希望该行为影响子类?