装饰器...:
例如:
@decoA
@decoB
@decoC
def myFunc(...): ...
...
等同于:
def myFunc(...): ...
...
myFunc = decoA(decoB(decoC(myFunc))) #note the *ordering*
多重继承...:
__init__
方法将不会被调用,除非显式调用它(通过方法解析顺序协议)!总之,如果装饰器不返回代理对象,我会使用装饰器进行混合(mixin)行为。其中一些示例包括任何返回经过略微修改的原始函数(或在某处注册或将其添加到某个集合后)的装饰器。
常见的装饰器主要用于记忆(memoization)等方面也是一个不错的选择,但是如果它们返回代理对象,则应该适度使用;应用它们的顺序很重要。将太多的装饰器叠加在一起就像滥用了它们本不想要的方式。
如果问题是“传统继承问题”,或者如果我所需要的混入(mixin)行为仅涉及方法,则可以考虑使用继承。传统的继承问题是指您可以在任何可以使用父类的地方使用子类。
总的来说,我尽可能编写不需要增强任意事物的代码。
issubclass
和 isinstance
。如果你需要这些功能,那么你可能需要使用继承。这就是使用继承而不是装饰器实现 Collections.* 的合理性所在。 - Neil G__init__
都会看到一个修改后的对象。 - Neil Gdataclass
应该成为一个mixin,以公开特定于 dataclass 的内省变量,如 dataclass 字段列表。@decorator
class A:
... # Can be affected by the decorator.
class B(A):
... # Not affected by the decorator in most cases.
现在 Python 有了 __init_subclass__
,所有装饰器能做的事情都可以通过 mixin 来完成,并且它们通常会影响子类:
class A(Mixin):
... # Is affected by Mixin.__init_subclass__.
class B(A):
... # Is affected by Mixin.__init_subclass__.
个人而言,我更倾向于考虑代码重用。装饰器有时比继承更灵活。
让我们以缓存为例。如果您想要将缓存功能添加到系统中的两个类A和B中,使用继承,您可能最终会拥有ACached和BCached。并且通过覆盖这些类中的一些方法,您可能会为相同的缓存逻辑重复大量代码。但是如果在这种情况下使用装饰器,则只需要定义一个装饰器来装饰两个类。
因此,在决定使用哪种方法时,您可能首先要检查扩展功能是否仅特定于此类,或者是否可以在系统的其他部分重用相同的扩展功能。如果不能重用,则继承可能是合适的选择。否则,您可以考虑使用装饰器。