<p>Carcigenicate已经提供了一个很好的答案。我只是想补充一下这个部分:</p>
<blockquote>
<p>I'm struggling to understand why (composition) is preferred over inheritance.</p>
</blockquote>
<p>实际上,它是关于组合/委托的,而不仅仅是组合(组合本身并不提供相同的特性)。关键是继承实际上有两个目的:子类型化和实现重用</p>
<p>子类型表示“是a”关系-如果B是a的(适当的)子类型,则可以在任何可以使用a的地方使用B。实际上,Liskov替换原则反过来说:“如果任何接受a的代码都可以接受B,则B是a的适当子类型”。请注意,这并没有说明继承和实现的任何内容,而且这些都不是子类型化所必需的(在理论层面上)</p>
<p>现在,对于静态类型语言,<em>必须</em>使用继承来进行子类型化,period-如果至少有任何实现需要重用的话,您最终会得到实现重用作为奖励</p>
<p>动态类型化的OTHO-Python不需要继承子类型(当然,只要使用对象的代码不进行任何类型检查)——只要有一个兼容的接口就足够了。因此,在Python中,继承主要与实现重用有关</p>
<p>现在,从技术上讲,通过继承实现重用是一种组合/委托的形式——您的对象是它自己的类的实例,也是它所有超类的实例,并且无论在您的实例本身或它的类上没有解析的属性都将在父类上查找。“手动”组合/委派的主要区别在于继承受到更严格的限制——例如,您不能在运行时更改委派对象,也不能基于每个实例更改委派对象(嗯……在Python中,<em>实际上可以</em>,从技术上讲,可以在运行时更改实例的类,但实际上这是一个非常<em>的坏主意,从来没有像预期的那样工作过——在这里,就这样做了xD)</p>
<p>因此,wrt/实现重用、继承是一种受限的、主要是静态的组合/委托形式。这对于相当多的用例来说是好的,通常是框架或多或少需要继承的抽象基类等,但是其他一些情况可以用更动态的解决方案更好地解决(典型的例子是状态和策略设计模式,但还有很多其他模式)</p>
<p>此外,即使继承仅用于实现重用,继承仍然意味着一种“是一种”关系——即使子类不是它的基的适当子类型(任何不兼容都会破坏适当的子类型,并且没有任何东西阻止您使用不兼容的签名更改某些子类方法),Python也没有任何概念“私有继承”,您的子类将公开其继承的所有接口,这不一定是您想要的(实际上,在执行实现重用时,通常是您不想要的)。当然,如果您决定更改要重用的实现,那么……继承引入了更强的耦合(这是一种轻描淡写的说法)而不是组成/授权</p>
<p>一个典型的“初学者错误”示例是从某个内置集合类型(例如<code>list</code>)继承,并尝试将其“限制”到特定的需要。这从来没有像预期的那样有效,通常需要比使用组合/委派多得多的工作。然后他们意识到这一点(仍然是例如)对于他们自己的用例来说,OrderedDict会是一个更好的基础,然后他们会遇到一个问题,客户机代码现在依赖于继承的<code>list</code>接口…从一开始就使用组合/委派可以避免很多麻烦——将接口限制在相关特性上,而不是泄漏继承的接口,从而保持“实现重用”真正的意义在于:客户机代码永远不应该知道的实现细节</p>
<p>T核心问题实际上是太多非常糟糕的“OO 101”文本将继承作为关键OO特性之一(事实并非如此——真正的“OO关键特性”是封装——不要与数据隐藏BTW和基于类型的多态调度混淆),导致初学者试图过度使用继承,却从未意识到还有其他更好的解决方案</p>
<p>长话短说:就像任何其他“黄金法则”一样,当你不了解每种解决方案的利弊,并且每种解决方案都更合适时,支持组合/委派而不是继承只是一条“法则”——瞧,就像任何“黄金法则”一样,你不想盲目地接受和应用它(这将导致愚蠢的设计选择),但是——正如你正确地做的那样——质疑它,直到你理解它的真正含义,甚至不必再去想它</p>
<p>哦,是的:您可能还想了解一下<a href="https://docs.python.org/3/reference/datamodel.html#object.__getattr__" rel="nofollow noreferrer">the ^{<cd3>} magic method</a>(用于委派)和<a href="https://docs.python.org/3/reference/datamodel.html#implementing-descriptors" rel="nofollow noreferrer">the descriptor protocol</a>以及内置<code>property</code>类型(用于计算属性支持)(提示:Python中不需要这些私有属性/公共访问器)</p>