答案 0 :(得分:19)
...装饰:
e.g。
@decoA
@decoB
@decoC
def myFunc(...): ...
...
相当于:
def myFunc(...): ...
...
myFunc = decoA(decoB(decoC(myFunc))) #note the *ordering*
多重继承......:
__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,以便公开特定于数据类的自省变量,例如数据类字段的列表。
装饰器。例如,一个装饰器从另一个类传播文档,或在某个集合中注册一个类。
装饰通常仅影响其装饰的类,而不影响从基类继承的类:
@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
吗?