当前位置: 首页 > news >正文

从零开始:Python语言进阶之继承

一、继承的基本概念

 

1.1 什么是继承

 

继承是指一个类(子类,也称为派生类)可以获取另一个类(父类,也称为基类或超类)的属性和方法。通过继承,子类无需重复编写父类已有的代码,从而实现代码的复用。例如,我们可以定义一个“动物”类作为父类,它包含动物共有的属性(如名称)和方法(如发出声音),然后创建“狗”类和“猫”类作为子类,它们继承“动物”类的属性和方法,并可以添加各自特有的属性和方法 。

 

1.2 继承的作用

 

①代码复用:避免在子类中重复编写父类已实现的功能,减少代码冗余。

②建立层次结构:清晰地表达类之间的关系,使程序结构更加直观。子类可以在继承父类的基础上,进行功能的扩展和修改,满足不同的需求。

 

二、单继承的实现

 

2.1 单继承的语法

 

在Python中,定义子类继承父类的语法如下:

class 子类名(父类名):# 子类的属性和方法定义pass

例如,定义一个“动物”类作为父类,再定义“狗”类继承自“动物”类:

# 定义父类Animal
class Animal:def __init__(self, name):self.name = namedef speak(self):print("The animal makes a sound")# 定义子类Dog继承自Animal
class Dog(Animal):def bark(self):print(f"{self.name} says Woof!")# 创建Dog类的实例
my_dog = Dog("Buddy")
my_dog.speak()  # 调用从父类继承的方法
my_dog.bark()  # 调用子类特有的方法

在上述代码中, Dog 类继承了 Animal 类的 __init__ 方法和 speak 方法,同时定义了自己特有的 bark 方法。

 

2.2 方法重写

 

当子类需要对父类的某个方法进行特殊实现时,可以在子类中重新定义该方法,这就是方法重写。例如,让“狗”类的 speak 方法有不同于父类的表现:

class Dog(Animal):def bark(self):print(f"{self.name} says Woof!")def speak(self):self.bark()my_dog = Dog("Buddy")
my_dog.speak()  # 输出:Buddy says Woof!

此时,调用 my_dog.speak() 时,执行的是子类重写后的 speak 方法。

 

2.3 调用父类方法

 

在子类中,如果需要在重写的方法中保留父类方法的部分功能,可以使用 super() 函数来调用父类的方法。 super() 会根据方法解析顺序(后面会详细介绍)找到对应的父类方法。例如:

class Animal:def __init__(self, name):self.name = namedef speak(self):print("The animal makes a sound")class Dog(Animal):def speak(self):super().speak()  # 先调用父类的speak方法print(f"{self.name} says Woof!")my_dog = Dog("Buddy")
my_dog.speak() 
# 输出:
# The animal makes a sound
# Buddy says Woof!

super().speak() 调用了父类 Animal 的 speak 方法,然后子类再添加自己特有的输出内容。

 

三、方法解析顺序(MRO)

 

3.1 什么是MRO

 

方法解析顺序(Method Resolution Order,MRO)决定了Python在调用方法时,查找方法的顺序。当一个类继承自多个类(多重继承时会详细介绍),或者子类重写了父类方法,Python需要按照一定的规则确定调用哪个类中的方法,这个规则就是MRO。

 

3.2 查看MRO

 

每个类都有一个 __mro__ 属性,通过它可以查看类的方法解析顺序。例如:

class A:def func(self):print("Method in A")class B(A):def func(self):print("Method in B")class C(B):def func(self):print("Method in C")print(C.__mro__)
# 输出:(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

从输出结果可以看出,当在 C 类的实例上调用 func 方法时,Python会先在 C 类中查找,若找不到则依次在 B 类、 A 类中查找,最后在基类 object 中查找(在Python中,所有类默认继承自 object 类)。

 

四、多重继承

 

4.1 多重继承的语法

 

Python允许一个子类继承多个父类,语法如下:

class 子类名(父类1, 父类2,...):# 子类的属性和方法定义pass

例如,定义一个“会飞的动物”类和“会游泳的动物”类,然后创建“鸭子”类继承这两个类:

class Flyable:def fly(self):print("I can fly!")class Swimmable:def swim(self):print("I can swim!")class Duck(Flyable, Swimmable):def __init__(self, name):self.name = nameduck = Duck("Donald")
duck.fly()
duck.swim()

Duck 类同时拥有了 Flyable 类的 fly 方法和 Swimmable 类的 swim 方法。
 
4.2 多重继承的问题与解决
 
多重继承虽然强大,但也容易引发一些问题,比如多个父类存在同名方法时,Python如何确定调用顺序?这就依赖于前面提到的MRO。如果不理解MRO,可能会导致调用的方法不符合预期。此外,多重继承还可能导致代码结构复杂,难以维护。
 
为了避免多重继承带来的复杂性,在实际编程中应谨慎使用。如果确实需要实现类似功能,可以考虑使用组合(一个类中包含其他类的实例作为属性)等替代方案,降低类之间的耦合度 。
 
五、继承常见基础问题及解决
 
5.1 忘记调用父类的 __init__ 方法
 
在子类的 __init__ 方法中,如果需要初始化从父类继承的属性,必须调用父类的 __init__ 方法。否则,父类的属性将不会被正确初始化。例如:

class Animal:def __init__(self, name):self.name = namedef speak(self):print("The animal makes a sound")class Dog(Animal):def __init__(self):# 错误写法,没有调用父类的__init__方法passdef bark(self):print(f"{self.name} says Woof!")my_dog = Dog()
# 运行会报错,因为self.name没有被初始化
my_dog.bark() 

正确的做法是使用 super() 调用父类的 __init__ 方法:

class Dog(Animal):def __init__(self, name):super().__init__(name)  # 调用父类的__init__方法def bark(self):print(f"{self.name} says Woof!")my_dog = Dog("Buddy")
my_dog.bark()  # 输出:Buddy says Woof!

5.2 方法重写时的逻辑错误

 

在重写父类方法时,如果没有正确处理逻辑,可能会导致功能异常。例如,重写后的方法没有实现父类方法的全部功能,或者引入了新的错误。在重写方法时,要仔细考虑是否需要保留父类方法的部分逻辑,并通过 super() 进行调用。

 

5.3 继承层次过深

 

如果继承层次过深,会使代码的可读性和维护性变差。当需要修改某个功能时,很难快速定位到相关代码。为了避免这个问题,应尽量保持继承层次简洁,合理划分类的职责。如果发现继承层次过深,可以考虑重构代码,使用组合、接口等方式优化结构。

 

六、总结

 

继承是Python面向对象编程的重要特性,掌握单继承、方法重写、MRO以及多重继承等基础知识,是深入学习Python进阶编程的关键。同时,了解继承过程中常见的问题,并掌握相应的解决方法,有助于编写出结构清晰、可维护性强的代码。在实际编程中,要根据具体需求合理运用继承,结合其他编程技巧,提升代码质量和开发效率。

感谢大家的关注,正在持续更新中....

http://www.xdnf.cn/news/604729.html

相关文章:

  • window 显示驱动开发-视频内存供应和回收(二)
  • 计算机语言&计算机安全知识
  • 十、Linux 网络服务基础
  • NLweb本地部署指南
  • EasyRTC音视频实时通话WebP2P技术赋能的全场景实时通信解决方案
  • 数据分析概述and环境配置
  • 照片时光机APP:修复老照片,重现往昔美好
  • Windows逆向工程提升之IMAGE_EXPORT_DIRECTORY
  • Git和Gitcode交互教程
  • 85. Java Record 深入解析:构造函数、访问器、序列化与实际应用
  • 关于千兆网络变压器的详细介绍
  • 【Flutter】多语言适配-波斯语RTL从右到左
  • 基于 Vue3 与 exceljs 实现自定义导出 Excel 模板
  • 如何在Mac 上使用Python Matplotlib
  • Redis 详解
  • G1人形机器人软硬件组成
  • vite学习笔记
  • Jenkins 2.426.2配置“构建历史的显示名称,加上包名等信息“
  • 计算机网络——每一层的用到的设备及其作用
  • Spring MVC-面试题(33)
  • Python asyncio库:基本概念与使用方法
  • voc怎么转yolo,如何分割数据集为验证集,怎样检测CUDA可用性 并使用yolov8训练安全帽数据集且构建基于yolov8深度学习的安全帽检测系统
  • React+MapBox GL JS引入URL服务地址实现自定义图标标记地点、区域绘制功能
  • vue 鼠标经过时显示/隐藏其他元素
  • FPGA高效验证工具Solidify 8.0:全面重构图形用户界面
  • 游戏引擎学习第306天:图结构排序的调试
  • QT-VStudio2107加载项目,报出“元素 <LanguageStandard>只有无效值“Default“”
  • ten-vad:低延迟、轻量化且高性能的流式语音活动检测系统
  • 2025年5月网工基础知识
  • rosbridge_suit、roslibpy 源码阅读与简单测试 —— 图片编解码与传输