装饰器与继承
How do you decide between using decorators and inheritance when both are possible?
E.g., this problem has two solutions.
I'm particularly interested in Python.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
装饰器...:
例如
相当于:
多重继承...:
__init__
方法将不会被调用,除非显式调用它(通过方法-解析-顺序协议)!总而言之,如果装饰器不返回代理对象,我会使用装饰器来实现类似 mixin 的行为。一些示例将包括返回原始函数的任何装饰器,稍作修改(或者在将其注册到某个地方或将其添加到某个集合之后)。
您经常会找到装饰器的东西(例如记忆)也是不错的候选者,但如果它们返回代理对象,则应适度使用;它们的应用顺序很重要。太多的装饰器以一种不应该使用的方式相互使用它们。
如果这是一个“经典继承问题”,或者如果我混合行为所需的只是方法,我会考虑使用继承。一个经典的继承问题是,只要可以使用父级,就可以使用子级。
一般来说,我尝试编写不需要增强任意事物的代码。
Decorators...:
e.g.
Is equivalent to:
Multiple inheritance...:
__init__
method will not be called unless it is called explicitly (via the method-resolution-order protocol)!To sum up, I would use decorators for mixin-like behavior if they didn't return proxy objects. Some examples would include any decorator which returns the original function, slightly modified (or after registering it somewhere or adding it to some collection).
Things you will often find decorators for (like memoization) are also good candidates, but should be used in moderation if they return proxy objects; the order they are applied matter. And too many decorators on top of one another is using them in a way they aren't intended to be used.
I would consider using inheritance if it was a "classic inheritance problem", or if all I needed for the mixin behavior were methods. A classic inheritance problem is one where you can use the child wherever you could use the parent.
In general, I try to write code where it is not necessary to enhance arbitrary things.
其他答案都很好,但我想给出一个简洁的优缺点列表。
mixins 的主要优点是可以使用 isinstance 在运行时检查类型,并且可以使用 MyPy 等 linter 进行检查。与所有继承一样,当您具有is-a关系时应该使用它。例如,
dataclass
可能应该是一个 mixin,以便公开特定于数据类的自省变量,例如数据类字段列表。当您没有is-a关系时,应该首选装饰器。例如,从另一个类传播文档或在某个集合中注册类的装饰器。
装饰通常只影响它所装饰的类,而不影响从基类继承的类:
现在Python有了__init_subclass__,装饰器可以做的所有事情都可以通过mixin完成,而且它们通常会影响子类:
Mixin还有另一个优点,那就是它们可以提供空的基类方法。子类可以用一些“增强”行为重写这些方法,然后调用 super.装饰器不能轻易提供这样的基类方法。这是 mixin 更加灵活的另一种方式。
总之,在决定 mixin 和装饰时应该问的问题是:
isinstance
吗?一般来说,倾向于继承。
The other answers are quite great, but I wanted to give a succinct list of pros and cons.
The main advantage of mixins is that the type can be checked at runtime using
isinstance
and it can be checked with linters like MyPy. Like all inheritance, it should be used when you have an is-a relationship. For exampledataclass
should probably have been a mixin in order to expose dataclass-specific introspection variables like the list of dataclass fields.Decorators should be preferred when you don't have an is-a relationship. For example, a decorator that propagates documentation from another class, or registers a class in some collection.
Decoration typically only affects the class it decorates, but not classes that inherit from the base class:
Now that Python has
__init_subclass__
, everything that decorators can do can be done with mixins, and they typically do affect child subclasses:Mixins have another advantage, which is that they can provide empty base class methods. Child classes can override these methods with some "augmenting" behavior, and then call super. The decorator cannot easily provide such base class methods. This is another way in which mixins are more flexible.
In summary, the questions you should ask when deciding between a mixin and decoration are:
isinstance
?In general, lean towards inheritance.
您引用的问题不是在装饰器和类之间做出决定。它正在使用装饰器,但您可以选择使用以下任一者:
装饰器只是“包装”模式的一个奇特名称,即用某物替换某物。实现取决于您(类或函数)。
在它们之间做出决定时,这完全取决于个人喜好。你可以与另一个人一起做你能做的一切。
(为什么这是一个好主意?可能会假设装饰函数仍然是一个函数,并且装饰函数仍然是一个函数)类仍然是一个类。)
在这两种情况下,更好的是使用一个装饰器,它只返回原始的、经过某种方式修改的。
编辑:在更好地理解您的问题后,我在 类的 Python functools.wraps 等效项
The problem you reference is not deciding between decorators and classes. It is using decorators, but you have the option of using either:
A decorator is just a fancy name for the "wrapper" pattern, i.e. replacing something with something else. The implementation is up to you (class or function).
When deciding between them, it's completely a matter of personal preference. You can do everything you can do in one with the other.
(Why is it a good idea? There may be assumptions that a decorated function is still a function, and a decorated class is still a class.)
Even better in both cases would be to use a decorator which just returns the original, modified somehow.
edit: After better understanding your question, I have posted another solution at Python functools.wraps equivalent for classes
如果两者相等,我更喜欢装饰器,因为您可以对许多类使用相同的装饰器,而继承仅适用于一个特定的类。
If both are equivalent, I would prefer decorators, since you can use the same decorator for many classes, while inheriting apply to only one specific class.
就我个人而言,我会考虑代码重用。装饰器有时比继承更灵活。
我们以缓存为例。如果您想通过继承向系统中的两个类添加缓存功能:A 和 B,您可能最终会拥有 ACached 和 BCached。通过重写这些类中的一些方法,您可能会为相同的缓存逻辑重复许多代码。但如果在这种情况下使用装饰器,则只需要定义一个装饰器来装饰这两个类。
因此,在决定使用哪一个时,您可能首先要检查扩展功能是否仅特定于此类,或者相同的扩展功能是否可以在系统的其他部分中重用。如果它不能被重用,那么继承可能可以完成这项工作。否则,你可以考虑使用装饰器。
Personally, I would think in terms of code reuse. Decorator is sometimes more flexible than inheritance.
Let's take caching as an example. If you want to add caching facility to two classes in your system: A and B, with inheritance, you'll probably wind up having ACached and BCached. And by overriding some of the methods in these classes, you'll probably duplicate a lot of codes for the same caching logic. But if you use decorator in this case, you only need to define one decorator to decorate both classes.
So, when deciding which one to use, you may first want to check if the extended functionality is only specific to this class or if the same extended functionality can be reused in other parts of your system. If it cannot be reused, then inheritance should probably do the job. Otherwise, you can think about using decorator.