装饰器与继承

发布于 2024-11-16 05:34:03 字数 180 浏览 4 评论 0原文

当装饰器和继承都可行时,您如何决定使用两者?

例如,这个问题有两种解决方案。

我对Python特别感兴趣。

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

怪我太投入 2024-11-23 05:34:03

装饰器...:

  • ...如果您想要做的是“包装”,则应该使用。包装包括获取某些内容、修改(或将其注册到某些内容)和/或返回一个行为“几乎完全”与原始对象相同的代理对象。
  • ...只要您不创建大量代理对象,就可以应用类似 mixin 的行为。
  • ...有一个隐含的“堆栈”抽象:

例如

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

相当于:

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

多重继承...:

  • ...最适合向类添加方法;你不能轻易地用它来装饰函数。在这种情况下,如果您只需要一组“鸭子类型”额外方法,它可以用于实现类似 mixin 的行为。
  • ...如果您的问题与超类构造函数等问题不太匹配,则可能有点难以处理。例如,子类 __init__ 方法将不会被调用,除非显式调用它(通过方法-解析-顺序协议)!

总而言之,如果装饰器不返回代理对象,我会使用装饰器来实现类似 mixin 的行为。一些示例将包括返回原始函数的任何装饰器,稍作修改(或者在将其注册到某个地方或将其添加到某个集合之后)。

您经常会找到装饰器的东西(例如记忆)也是不错的候选者,但如果它们返回代理对象,则应适度使用;它们的应用顺序很重要。太多的装饰器以一种不应该使用的方式相互使用它们。

如果这是一个“经典继承问题”,或者如果我混合行为所需的只是方法,我会考虑使用继承。一个经典的继承问题是,只要可以使用父级,就可以使用子级。

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

Decorators...:

  • ...should be used if what you are trying to do is "wrapping". Wrapping consists of taking something, modifying (or registering it with something), and/or returning a proxy object that behaves "almost exactly" like the original.
  • ...are okay for applying mixin-like behavior, as long as you aren't creating a large stack of proxy objects.
  • ...have an implied "stack" abstraction:

e.g.

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

Is equivalent to:

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

Multiple inheritance...:

  • ... is best for adding methods to classes; you cannot use it to decorate functions easily. In this context, it can be used to achieve mixin-like behavior if all you need is a set of "duck-typing style" extra methods.
  • ... may be a bit unwieldy if your problem is not a good match for it, with issues with superclass constructors, etc. For example, the subclasses __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.

ゝ偶尔ゞ 2024-11-23 05:34:03

其他答案都很好,但我想给出一个简洁的优缺点列表。

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__,装饰器可以做的所有事情都可以通过mixin完成,而且它们通常会影响子类:

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

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

Mixin还有另一个优点,那就是它们可以提供空的基类方法。子类可以用一些“增强”行为重写这些方法,然后调用 super.装饰器不能轻易提供这样的基类方法。这是 mixin 更加灵活的另一种方式。

总之,在决定 mixin 和装饰时应该问的问题是:

  • 是否存在 is-a 模式?
    • 您会调用 isinstance 吗?
    • 您会在类型注释中使用 mixin 吗?
  • 您希望这种行为影响孩子们的班级吗?
  • 您需要增强方法吗?

一般来说,倾向于继承。

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 example dataclass 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:

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

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

Now that Python has __init_subclass__, everything that decorators can do can be done with mixins, and they typically do affect child subclasses:

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

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

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:

  • Is there an is-a pattern?
    • Would you ever call isinstance?
    • Would you use the mixin in a type annotation?
  • Do you want the behavior to affect child classes?
  • Do you need augmenting methods?

In general, lean towards inheritance.

你的背包 2024-11-23 05:34:03

您引用的问题不是在装饰器和类之间做出决定。它正在使用装饰器,但您可以选择使用以下任一者:

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

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

在它们之间做出决定时,这完全取决于个人喜好。你可以与另一个人一起做你能做的一切。

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

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

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

编辑:在更好地理解您的问题后,我在 类的 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, which returns a class
  • a decorator, which returns a function

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.

  • if decorating a function, you may prefer decorators which return proxy functions
  • if decorating a class, you may prefer decorators which return proxy classes

(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

秉烛思 2024-11-23 05:34:03

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

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.

秋意浓 2024-11-23 05:34:03

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

我们以缓存为例。如果您想通过继承向系统中的两个类添加缓存功能: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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文