python中的单继承和多继承

python中的单继承和多继承

1. 继承

1.1 继承的概念

继承:可以实现代码重用,相同的代码不需要重复编写。

1.2 为什么需要继承

我们有以下几个类:Animal:其中封装了drink, eat, run 和sleep四个方法, Dog:其中也封装了eat, drink, run, sleep, 除此之外还有一个bark方法。如果不使用继承,我们需要定义两个类,其中需要封装所有的类中包含的方法,这样就需要我们重复写eat, drink, run, sleep着四个方法。再来看XiaoTianQuan这个类,其内部有Dog类中的五个方法,仅仅多了一个fly方法,需要重复写狗类中的五个方法。同时,如果我们需要修改Animal类中的eat方法,我们需要同时修改Dog类中和XiaoTianQuan类中的eat方法。所以我们需要使用继承特性,使相同的代码不需要重复编写。

1.3 继承的语法格式

在上面的图中,我们可以把Dog看成子类,Animal看成父类,子类具有父类中所有的属性和方法,子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发,子类中应该根据职责,封装子类特有的属性和方法。
继承的使用方法如下

class 子类(父类):
    pass

以上图为例,我们利用继承创建一个Animal父类和一个Dog子类

class Animal:

    def drink(self):
        print("drink")

    def eat(self):
        print("eat")

    def run(self):
        print("run")

    def sleep(self):
        print("sleep")

class Dog(Animal):

    def bark(self):
        print("bark")

dog = Dog()
dog.eat()
dog.run()
dog.bark()
dog.drink()
dog.sleep()

在Dog类中,我们虽然没有写eat, run, drink, sleep方法,但是可以继承Animal类中的这些方法。
对于Dog类和Aniamal类,我们可以说Dog类是Animal类的子类,Animal类是Dog类的父类,Dog类从Animal类中继承。或者说Dog类是Animal类的派生类,Animal类是Dog类的基类,Dog类从Animal类中派生。

1.4 继承的特点

1.4.1 继承的传递性

C类从B类中继承,B类从A类中继承,那么C类就拥有B类和A类中所有的属性和方法。也就是说子类中拥有父类以及父类的父类中的所有封装的属性和方法。

1.4.2 方法的重写

子类继承父类中的所有属性和方法,可以直接享有父类中已经封装好的熟悉和方法,不需要再次开发。
但是在实际情况中,如果父类的方法实现不能满足子类的需求时,就需要对父类的方法进行重写(override)。

例如在上面的类图中,Dog类继承自Animal类,然后XiaoTianQuan类继承在Dog类,但是XiaoTianQuan类中的bark()方法和Dog类中的bark()方法不一样,这时我们就需要在定义XiaoTianQuan类时对bark()方法进行重写。
重新父类的方法有两种形式:

  1. 覆盖父类的方法 :
    如果在开发过程中,父类的方法实现和子类的方法实现完全不同就可以用覆盖的方式,在子类中重新编写父类的方法实现。具体实现的方式就相当于在子类中重新定义了一个和父类同名的方法并且实现,重写之后在运行时只会调用子类中的重新的方法,而不会再调用父类封装的方法。
    下面是用覆盖父类的方法的代码示例:
class Animal:

    def eat(self):

        print("eat")

    def drink(self):

        print("drink")

    def sleep(self):

        print("sleep")

    def run(self):

        print("run")


class Dog(Animal):

    def bark(self):

        print("bark")


class XiaoTianQuan(Dog):

    def fly(self):

        print("fly")

    def bark(self):

        print("xiaotianquan fly")


xtq = XiaoTianQuan()

xtq.bark()

运行输出“xiaotianquan fly”而不是“fly”,说明子类调用了自己的方法而不是父类的方法
2. 对父类的方法进行扩展:
如果在开发过程中,子类的方法实现中包含父类的方法实现,也就是说原本父类封装的方法实现是子类方法的一部分,这时候就可以使用扩展的方法。对父类方法进行扩展有以下3个步骤:

  1. 在子类中重写父类的方法
  2. 在需要的位置使用super().父类方法来调用父类方法的执行
  3. 代码其他的位置针对子类的需求,编写子类特有的代码实现
    下面是将上面对覆盖父类方法更改为在子类中重写父类方法代码:
class Animal:

    def eat(self):

        print("eat")

    def drink(self):

        print("drink")

    def sleep(self):

        print("sleep")

    def run(self):

        print("run")


class Dog(Animal):

    def bark(self):

        print("bark")


class XiaoTianQuan(Dog):

    def fly(self):

        print("fly")

    def bark(self):

        # 在子类中重写父类的方法
        print("xiaotianquan fly")

        # 在需要的位置使用super().父类方法来调用父类方法的执行
        super().bark()

        # 代码其他的位置针对子类的需求,编写子类特有的代码实现
        print("子类特有的代码实现")

运行上述代码可以输出以下结果:

xiaotianquan fly
bark

子类特有的代码实现
关于 super

  1. 在 Python 中 super 是一个 特殊的类
  2. super() 就是使用 super 类创建出来的对象
  3. 最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现

1.4.3 父类的私有属性和私有方法

子类对象不能在自己的方法内部直接访问父类的私有属性或私有方法,子类对象可以通过父类的公有方法间接访问到私有属性或私有方法。

2. 单继承

单继承是指一个类只能继承自一个父类。单继承可以帮助代码更简单、易于维护。

2.1 单继承语法

class 子类名(父类名):
    pass

下面是一个单继承的示例:

# 定义父类
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return f"{self.name} makes a sound"

# 定义子类,继承自 Animal
class Dog(Animal):
    def speak(self):
        return f"{self.name} barks"

# 实例化子类
dog = Dog("Buddy")
print(dog.speak())  # 输出: Buddy barks

3. 多继承

多继承是指一个类可以从多个父类继承属性和方法。多继承允许子类同时继承多个父类的功能。然而,多继承可能会带来一些复杂性,尤其是在多个父类中存在相同的方法时,Python 通过方法解析顺序(MRO)来决定调用哪个方法。

3.1 多继承语法

class 子类名(父类名1, 父类名2, ...):
    pass

下面是一个多继承的示例:

# 定义父类 A
class A:
    def speak(self):
        return "A speaks"

# 定义父类 B
class B:
    def speak(self):
        return "B speaks"

# 定义子类 C,同时继承自 A 和 B
class C(A, B):
    def introduce(self):
        return "I am C"

# 实例化子类
c = C()
print(c.speak())  # 输出: A speaks (根据 MRO,优先调用 A 的方法)
print(c.introduce())  # 输出: I am C

C类可以同时继承A类和B类中的方法。

3.2 注意事项

如果不同的父类具有同名的方法或属性,那么子类在继承时应该尽量避免选择多继承。在下面的示例中,C类同时继承A类和B类,但是A类和B类中同时都有test()方法和demo()方法。在实际使用过程中,我们的C类到底应该继承哪个父类中的方法呢?在python中有一个MRO(method resolution order)方法搜索顺序内置属性,可以通过mro查看方法的搜索顺序,主要用于在多继承时判断方法和属性的调用路径。

class A:

    def demo(self):
        print("A demo 方法")

    def test(self):
        print("A test 方法")


class B:

    def demo(self):
        print("B demo 方法")

    def test(self):
        print("Atest 方法")


class C(A, B):

    pass


c = C()
c.demo()
c.test()

# 确定C类的调用方法
print(C.__mro__)

运行上面代码输出以下信息:

表示的在执行是首先查看方法是不是在C类中,如果不是继续向下查看是否在A类中,如果还是没有便继续向下查找,如果有的话就停止查找执行找到的方法。
从上面的输出可以看到最后的object类,object类为python中所有类的基类,提供有一些内置的属性和方法,可以使用dir函数查看。

4. 总结

4.1 单继承的优缺点

优点

  1. 简单性:
    • 代码结构简单,易于理解和维护。每个子类只继承一个父类,因此继承关系非常清晰,不会出现方法冲突的复杂情况。
  2. 低耦合:
    • 父类和子类之间的关系更加紧密,代码耦合度较低,降低了类之间的复杂依赖。
  3. 调试容易:
    • 在调试时,只需考虑子类和一个父类之间的继承链,减少了复杂的调试步骤。
  4. 明确的责任分配:
    • 父类与子类之间的职责和功能更加明确,代码结构直观。

缺点

  1. 功能扩展受限:
    • 如果需要从多个类中继承不同的功能,单继承可能显得局限,需要通过合成、委托等其他方式来扩展功能。
  2. 代码复用有限:
    • 由于只能继承一个父类,可能导致代码的重复实现,不能像多继承那样灵活地复用多个类的功能。
  3. 不适合复杂系统:
    • 在需要复杂功能集成的系统中,单继承可能需要大量的辅助类,导致代码冗长和不灵活。

4.2 多继承的优缺点

优点

  1. 代码复用强:
    • 多继承允许一个子类继承多个父类的属性和方法,可以更加灵活地重用代码,避免重复实现。
  2. 功能扩展灵活:
    • 可以从多个类中继承功能,使得类之间可以更加容易地组合功能,满足复杂的系统需求。
  3. 类间组合丰富:
    • 通过多继承,可以在子类中灵活地组合不同的父类,实现更强大的类功能,而不必过度依赖单一类的继承链。

缺点

  1. 复杂性增加:
    • 多继承会增加类的复杂性,特别是在多个父类中存在相同的方法时,可能会引发冲突,增加理解和维护的难度。
  2. 方法解析顺序(MRO)复杂:
    • 多继承引入了 Python 的方法解析顺序(MRO),即 Python 如何决定从哪个父类调用方法。MRO 对于初学者可能难以理解,尤其在出现菱形继承时(多个父类共同继承自同一个类)。
  3. 调试困难:
    • 当多个父类间有复杂的继承关系时,调试和排错会变得更加困难,尤其是当父类中的方法出现冲突时。
  4. 增加耦合:
    • 由于子类继承了多个父类的行为,这些父类之间的变动可能影响子类的行为,导致代码之间的耦合性增强,维护变得更为复杂。

4.3 使用场景

  • 单继承:
    • 更适合简单、层次化明确的系统。比如,如果一个类只需要继承一个特定的功能,那么单继承是最佳选择。
  • 多继承:
    • 适合需要整合多个父类功能的大型系统。比如,在设计复杂的类库或框架时,通过多继承可以更好地组合不同的功能模块。

在实际应用中,如果多继承带来过多复杂性,可以考虑使用接口(通过抽象基类或协议)、组合、或委托模式来减少类之间的耦合性,同时保持代码的灵活性和可扩展性。

posted @   零の守墓人  阅读(60)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示