Press "Enter" to skip to content

Python继承的常见实践和陷阱:菱形问题,混合,和其他

继承,就像面向对象编程中的任何其他概念一样,允许开发人员重用代码并开发优雅且可扩展的软件解决方案。考虑到Python的庞大社区,已经开发了多个模块和逻辑。然而,与任何概念一样,继承也有一些常见的陷阱-例如“钻石问题”、“接口继承挑战”以及使用混入的问题。

Image by Author

继承是面向对象编程的基本概念,其中一个新类继承其父类,并重用父类中定义的所有方法和变量。因此,它促进了代码可重用性、可扩展性和模块化。

多重继承

多重继承允许一个类从多个基类继承属性和方法。这意味着派生类可以同时继承多个类。

class fly:  can_fly = Trueclass swim:  can_swim = Trueclass Duck(swim, fly):    passd = Duck()print(d.can_fly)   #Trueprint(d.can_swim)  #True

上面是多重继承的一个简单例子,以及它如何有所帮助。如果我们想编写一个关于鸟类的Python程序,并在类中独立地提及一些特色,然后在讨论特定的鸟类时希望重用它们。在鸭子实例中,它既可以游泳又可以飞行还可以行走。在这些情况下,多重继承是有用的。

有些编程语言不支持多重继承,虽然它为我们提供了重用代码的灵活性,但也可能引入新的问题,例如钻石问题,当一个类继承自多个具有共同基类的类时,可能会产生混淆。

Python继承的常见实践和陷阱:菱形问题,混合,和其他 四海 第2张

钻石问题

钻石问题特别在于子类继承自具有共同基类的多个父类。这会导致在执行被覆盖方法的顺序上产生歧义。让我们看下面的例子-

class Animal:    def speak(self):        print("动物在说话")class Bird(Animal):    def speak(self):        print("鸟在鸣叫")class Fish(Animal):    def speak(self):        print("鱼在冒泡")class Amphibian(Bird, Fish):    pass

这里Amphibian类继承了Fish和Bird类,它们都有一个共同的基类Animal。由于speak方法在两个父类中都被覆盖,这会导致Amphibian类在调用speak方法时产生混淆。因此,一些编程语言不支持多重继承。

但是Python通过使用C3线性化来解决钻石问题,使用方法解析顺序(MRO)来实现继承。

方法解析顺序(MRO)

在Python中,从2.3版本开始引入的C3线性化规定了方法解析顺序。它确定了查找方法或变量时要搜索类的顺序。C3线性化算法考虑了继承的顺序,并推导出类之间的关系。

当调用方法时,Python首先在当前类中搜索,如果未找到,则按照MRO在基类中搜索。对于多重继承,MRO将从左到右,对于多层继承,将从底部到顶部。这使得开发人员可以有效地使用多重继承,而不会产生任何歧义。

Python继承的常见实践和陷阱:菱形问题,混合,和其他 四海 第3张

现在,如果我们检查输出,Bird类中的speak首先被调用,因为它先被调用。

Amphibian().speak()# 输出 - 鸟在鸣叫

虽然MRO确保我们不会出现错误,但还可以使用混入来避免多继承并提供附加功能。

混入

混入是专注于单个功能的类。它们提供了一种在多个类之间共享功能的方式,而无需使用传统的继承。它们有助于防止钻石问题并避免深层次的继承层次结构。

class JSONMixin:    def to_json(self):        import json        return json.dumps(self.__dict__)class Person:    def __init__(self, name, age):        self.name = name        self.age = ageclass JSONPerson(Person, JSONMixin):    passperson = JSONPerson("Alice", 30)print(person.to_json())

其他继承陷阱

  • 继承类与基类紧密耦合,只要基类的功能发生变化,就必须测试所有继承类,以确保无误的代码。因此,组织基类和谨慎继承是必要的。在基类中维护包装器方法,并尽量不在子类中继承或更改静态代码。不选择脆弱和难以维护的基类。
  • 错误的方法重写或错误使用super()方法可能会产生容易出错的代码。在 Python 中,不强制封装和方法签名,因此如果没有正确地进行覆盖,可能会在运行时产生问题。
  • 继承 vs. 组合:继承并不是所有情况下的最佳解决方案,组合通常通过组合简单对象来创建复杂对象,而不是通过从子类继承行为。尽可能选择组合,因为它还促进代码可重用性和模块化,并避免继承中的问题,如钻石问题、紧密耦合和不灵活性。

这就是我关于常见继承陷阱的见解。希望对您有所帮助…愉快编码…

参考:https://www.python.org/download/releases/2.3/mro/

Leave a Reply

Your email address will not be published. Required fields are marked *