AI大模型学习02:Python继承和多态
今天继续面向对象的学习,主题是继承、多态、类成员等进阶知识。如果说第一天是学会“造一个独立的零件”,那么今天就是学会“把零件组装成家族,让它们互相协作”。
一、继承:为什么你的代码需要继承?
1.1 现实类比:基本款手机 vs. 旗舰款手机
想象一下:你买了一部基础款手机,它有“打电话”、“发短信”两个功能。后来你换了旗舰款,它自动拥有基础款的所有功能,还额外多了“人脸识别”、“无线充电”。
这就是继承:旗舰款(子类)继承了基础款(父类)的所有能力,并且可以扩展新能力。
在编程里,继承的价值只有两个字:复用。不用重复写同样的代码,改一处父类,所有子类自动更新。
1.2 核心术语(你只需要记住两个视角)
-
父类 / 基类:被继承的那个,相当于“基础款手机”。
-
子类 / 派生类:继承之后的新类,相当于“旗舰款手机”。
-
派生:站在父类的角度,看它“派生出”子类。
-
扩展:子类在父类之外,增加自己特有的属性或方法。不扩展的继承,意义不大。
二、单继承:一条直线传下去
2.1 什么是单继承?
一个子类只允许有一个父类。就像你只能有一个亲生父亲。
2.2 全新案例:智能家居设备
我们设计一个智能家居系统。先定义一个最通用的父类 SmartDevice(智能设备),它包含所有设备都有的属性和方法。
# 父类:所有智能设备的“模板”
class SmartDevice:
def __init__(self, device_name, brand):
self.device_name = device_name
self.brand = brand
def turn_on(self):
print(f"{self.brand} {self.device_name} 已开机")
def turn_off(self):
print(f"{self.brand} {self.device_name} 已关机")
现在,我们定义两个子类:Light(灯)和 AirConditioner(空调)。它们自动拥有 turn_on 和 turn_off 方法,不需要再写一遍。
# 子类1:灯,扩展一个“调节亮度”的方法
class Light(SmartDevice):
def adjust_brightness(self, level):
print(f"{self.brand} {self.device_name} 亮度调到 {level}%")
# 子类2:空调,扩展一个“设置温度”的方法
class AirConditioner(SmartDevice):
def set_temperature(self, temp):
print(f"{self.brand} {self.device_name} 温度设为 {temp}℃")
# 测试
bedroom_light = Light("卧室吸顶灯", "小米")
bedroom_light.turn_on() # 继承来的方法
bedroom_light.adjust_brightness(80) # 自己的扩展方法
ac = AirConditioner("客厅空调", "格力")
ac.turn_on() # 继承来的方法
ac.set_temperature(24) # 自己的扩展方法
输出:
小米 卧室吸顶灯 已开机
小米 卧室吸顶灯 亮度调到 80%
格力 客厅空调 已开机
格力 客厅空调 温度设为 24℃
你看,Light 和 AirConditioner 不需要写任何开关机的代码,全部从父类继承。这就是复用。
三、方法重写
3.1 为什么需要重写?
有时候父类的方法不够“个性化”。比如所有设备开机都是打印“已开机”,但空调开机后可能还需要自动设定一个默认温度。这时候子类就可以重写父类的方法。
3.2 就近原则
调用方法时,Python 的查找顺序:先看子类有没有这个方法,如果有就用子类的;如果没有,再去父类找。 这叫“就近原则”。
3.3 案例:空调重写开机方法
class AirConditioner(SmartDevice):
def turn_on(self): # 重写父类的 turn_on
print(f"{self.brand} {self.device_name} 已开机,当前室温26℃,开始制冷")
def set_temperature(self, temp):
print(f"{self.brand} {self.device_name} 温度设为 {temp}℃")
# 测试
ac = AirConditioner("书房空调", "美的")
ac.turn_on() # 输出:美的 书房空调 已开机,当前室温26℃,开始制冷
此时,ac.turn_on() 执行的是子类自己的版本,父类的版本被“覆盖”了。
3.4 重写后如何保留父类逻辑?
有时候你既想保留父类的通用行为,又想增加子类特有的行为。比如开机时先执行父类的“已开机”提示,再补充空调的温度信息。这就要用到 super()。
class AirConditioner(SmartDevice):
def turn_on(self):
# 先调用父类的 turn_on 方法
super().turn_on()
# 再添加子类的特有逻辑
print("当前室温26℃,开始制冷")
输出:
美的 书房空调 已开机
当前室温26℃,开始制冷
super() 是 Python 推荐的调用父类方法的方式,它不需要手动传 self,而且当继承结构复杂时(比如多继承)会自动处理调用顺序。
另一种方式是用父类名直接调用:SmartDevice.turn_on(self),但需要自己传 self,而且不够优雅。
四、多继承
4.1 语法和多继承场景
Python 允许一个子类同时继承多个父类,用逗号隔开。
比如一个“智能窗帘”既是“智能设备”又是“遮光设备”,就可以同时继承两个父类。
4.2 注意:多继承 ≠ 多层继承
-
多继承:一个子类有多个平级的父类。
class C(A, B) -
多层继承:一层层往下传。
class B(A),class C(B)
4.3 方法冲突与 MRO(方法解析顺序)
如果两个父类有同名方法,子类调用时到底执行哪个?答案是:看 MRO。
MRO 是 Python 计算出的一个线性顺序列表,你可以通过 类名.__mro__ 查看。
案例:智能窗帘同时继承“智能设备”和“定时设备”
class SmartDevice:
def get_status(self):
return "我是智能设备"
class TimerDevice:
def get_status(self):
return "我有定时功能"
class SmartCurtain(SmartDevice, TimerDevice):
pass
curtain = SmartCurtain()
print(curtain.get_status()) # 输出:我是智能设备
print(SmartCurtain.__mro__) # 查看顺序
输出结果说明 SmartDevice 排在 TimerDevice 前面,所以用了 SmartDevice 的 get_status。
五、多态
5.1 什么是多态?
多态是指:同一个操作(比如“开机”),作用在不同设备上,产生不同的效果。
你手里的万能遥控器,按“开/关”键,空调会调温,电视会换频道,灯会亮灭。这就是多态。
5.2 Python 的“鸭子类型”
在强类型语言(如 Java)中,多态必须有继承关系。但 Python 很随意:只要一个对象有你想调用的方法,不管它属于哪个类,都能正常执行。这叫“鸭子类型”——“如果它叫起来像鸭子,走路像鸭子,那它就是鸭子”。
案例:一个“智能音箱”对象,只要它有 turn_on 方法,就算它不是 SmartDevice 的子类,也能被同一个函数调用。
# 一个完全不相干的类,但恰好也有 turn_on 方法
class Speaker:
def turn_on(self):
print("智能音箱 已唤醒,等待指令")
def power_on(device):
device.turn_on() # 不管 device 是什么,只要它有 turn_on 就能用
power_on(Light("台灯", "飞利浦")) # 输出:飞利浦 台灯 已开机
power_on(Speaker()) # 输出:智能音箱 已唤醒,等待指令
如果你想强制只有某个类的子类才能调用,可以用 isinstance() 做类型检查:
def power_on(device):
if isinstance(device, SmartDevice):
device.turn_on()
else:
print("这不是智能设备,无法开机")
多态的最大好处是:你写函数时不需要关心具体类型,后期增加新设备也不会破坏原有代码。
六、属性与方法的“家族等级”
6.1 类属性&&实例属性
| 类型 | 定义位置 | 归属 | 内存 | 修改方式 |
|---|---|---|---|---|
| 实例属性 | __init__ 中用 self.xxx = 值 |
每个对象独有 | 每对象一份 | 对象.属性 = 新值 |
| 类属性 | 直接写在类里面,方法外面 | 类本身,所有对象共享 | 全类只有一份 | 类名.属性 = 新值 |
案例:统计智能设备的总数量
class SmartDevice:
total_devices = 0 # 类属性,所有设备共享
def __init__(self, name, brand):
self.name = name
self.brand = brand
SmartDevice.total_devices += 1 # 用类名修改
# 创建多个设备
light = Light("台灯", "飞利浦")
ac = AirConditioner("空调", "格力")
print(SmartDevice.total_devices) # 输出 2
易错:千万不要写成 self.total_devices += 1,因为如果实例没有 total_devices 属性,它会创建一个同名的实例属性,然后只修改实例自己的,类属性纹丝不动。务必用 类名.属性 来操作类属性。
6.2 实例方法 vs 类方法 vs 静态方法
-
实例方法:最常见的那种,第一个参数是
self,用来操作具体对象的属性。 -
类方法:装饰器
@classmethod,第一个参数是cls(类本身),用来操作类属性或做类级别的操作。 -
静态方法:装饰器
@staticmethod,没有self或cls,就像一个普通函数,只是放在类里面作为逻辑分组。
案例:给智能设备类增加一个类方法和一个静态方法
class SmartDevice:
total_devices = 0
def __init__(self, name):
self.name = name
SmartDevice.total_devices += 1
# 类方法:可以访问类属性 total_devices
@classmethod
def show_total(cls):
print(f"当前共有 {cls.total_devices} 台智能设备")
# 静态方法:与类状态无关,纯工具函数
@staticmethod
def convert_celsius_to_fahrenheit(c):
return c * 9/5 + 32
# 使用
SmartDevice.show_total() # 类方法,输出:当前共有 0 台设备
light = SmartDevice("台灯")
SmartDevice.show_total() # 输出:当前共有 1 台设备
print(SmartDevice.convert_celsius_to_fahrenheit(26)) # 78.8,静态方法
什么时候用类方法?
当你需要操作类属性(比如计数、配置项),或者需要创建类的另一种构造方式时。
什么时候用静态方法?
当方法逻辑和类、实例都无关,只是因为这个功能从属于这个类概念(比如温度转换对于空调来说很相关)。
七、私有属性与私有方法:给核心数据加把锁
7.1 为什么需要私有?
你不想让外部代码随意修改设备内部的敏感数据,比如“空调的压缩机运行密码”或者“灯的芯片固件版本”。这些数据应该只能由设备内部的方法来操作。
7.2 定义方式
在属性名或方法名前加双下划线 __。
7.3 案例:灯的“累计使用时长”不让外部直接改
class Light(SmartDevice):
def __init__(self, name, brand):
super().__init__(name, brand)
self.__total_usage_hours = 0 # 私有属性:累计使用时长
def turn_on(self):
super().turn_on()
# 内部可以访问私有属性
self.__total_usage_hours += 0.5
print(f"本次开机增加0.5小时,总使用 {self.__total_usage_hours} 小时")
# 提供公共的 getter 方法,让外部安全地读取
def get_usage_hours(self):
return self.__total_usage_hours
# 提供 setter 方法,并加入校验(比如不能设置为负数)
def set_usage_hours(self, hours):
if hours >= 0:
self.__total_usage_hours = hours
else:
print("使用时长不能为负数")
# 测试
lamp = Light("落地灯", "欧普")
lamp.turn_on() # 输出:... 增加0.5小时
print(lamp.get_usage_hours()) # 0.5
lamp.__total_usage_hours = 100 # 外部直接修改无效!Python会创建一个新的普通属性,不影响私有属性
print(lamp.get_usage_hours()) # 仍然是0.5
访问规则:
-
类内部:可以直接用
self.__私有属性。 -
类外部:不能直接访问(会报错
AttributeError)。 -
子类:也不能继承私有成员。
-
必须通过类内部提供的公共方法(get_xxx / set_xxx)间接访问。
八、抽象类:制定一个不可违背的“合同”
8.1 抽象类是什么?
抽象类是一个不能被实例化的类,它用来定义一组“子类必须实现的方法”。就像一份合同,签了合同的子类必须完成合同上的条款,否则无法创建对象。
8.2 为什么需要抽象类?
假设你有一个系统,里面有多种设备,你希望每个设备都必须实现“获取能耗报告”的功能。如果没有抽象类,某个程序员可能忘记写这个方法,导致程序崩溃。抽象类可以从语法层面强制要求。
8.3 语法(使用 Python 内置的 abc 模块)
from abc import ABC, abstractmethod
class Reportable(ABC):
@abstractmethod
def generate_report(self):
"""子类必须实现这个方法,返回报告字符串"""
pass
class EnergyMeter(Reportable):
def generate_report(self):
return "本月用电 120 度"
# 下面这行会报错,因为抽象类不能实例化
# r = Reportable()
em = EnergyMeter()
print(em.generate_report()) # 输出:本月用电 120 度
如果某个子类忘记实现 generate_report,那么创建它的对象时就会报错。这保证了所有设备都有统一的接口,方便统一调用。
九、【全新综合案例】智能家居自动化中心
我们把今天学的所有知识整合起来,设计一个“智能家居自动化中心”。
需求:
-
所有设备必须继承自
SmartDevice,并且强制实现status_report方法(抽象方法)。 -
用类属性统计设备总数。
-
每个设备有一个私有属性
_firmware_version,通过公共方法访问。 -
设备具有不同的“开机”行为(方法重写)。
-
编写一个自动化函数
auto_control,根据传入的设备列表,依次调用其execute_command方法(多态演示)。 -
定义一个
TimerMixin类(作为多继承的第二个父类),提供定时功能。
代码实现:
from abc import ABC, abstractmethod
# 抽象基类:所有设备必须实现 status_report
class SmartDevice(ABC):
total_devices = 0
def __init__(self, name, brand, firmware):
self.name = name
self.brand = brand
self.__firmware = firmware # 私有属性
SmartDevice.total_devices += 1
@abstractmethod
def status_report(self):
"""子类必须返回设备状态字符串"""
pass
def turn_on(self):
print(f"{self.brand} {self.name} 已开机")
def get_firmware(self):
return self.__firmware
def set_firmware(self, version):
if version.startswith("v"):
self.__firmware = version
else:
print("固件版本格式应为 v1.0 这样的形式")
# 多继承的第二个父类:提供定时功能
class TimerMixin:
def set_timer(self, seconds):
print(f"设定 {seconds} 秒后自动执行")
# 灯:重写 turn_on 并实现 status_report
class Light(SmartDevice, TimerMixin):
def turn_on(self):
super().turn_on()
print("灯光渐亮至80%")
def status_report(self):
return f"灯 {self.name} 固件{self.get_firmware()},工作正常"
# 插座:重写 turn_on 并实现 status_report
class SmartPlug(SmartDevice, TimerMixin):
def turn_on(self):
super().turn_on()
print("当前功率 0W,待机中")
def status_report(self):
return f"插座 {self.name} 固件{self.get_firmware()},电压稳定"
# 多态函数:不管传什么设备,只要它有 turn_on 和 status_report 就可以
def auto_control(devices):
print("===== 自动化控制开始 =====")
for device in devices:
device.turn_on()
print(device.status_report())
# 如果设备有定时能力(即继承了 TimerMixin),就调用它
if hasattr(device, "set_timer"):
device.set_timer(30)
print("---")
# 测试
lamp = Light("客厅主灯", "小米", "v2.1")
plug = SmartPlug("厨房插座", "公牛", "v1.0")
print(f"当前共有设备: {SmartDevice.total_devices} 个")
auto_control([lamp, plug])
# 查看 MRO(演示多继承顺序)
print(Light.__mro__)
输出示例:
当前共有设备: 2 个
===== 自动化控制开始 =====
小米 客厅主灯 已开机
灯光渐亮至80%
灯 客厅主灯 固件v2.1,工作正常
设定 30 秒后自动执行
---
公牛 厨房插座 已开机
当前功率 0W,待机中
插座 厨房插座 固件v1.0,电压稳定
设定 30 秒后自动执行
---
(<class '__main__.Light'>, <class '__main__.SmartDevice'>, <class '__main__.TimerMixin'>, <class 'abc.ABC'>, <class 'object'>)
这个案例覆盖了:
-
抽象类(强制
status_report) -
单继承(
Light继承SmartDevice) -
多继承(
Light同时继承SmartDevice和TimerMixin) -
方法重写(
turn_on) -
super()调用父类 -
类属性(
total_devices) -
私有属性 + getter/setter
-
多态(
auto_control函数不关心具体类型) -
MRO 查看
知识点速记卡
| 概念 | 一句话解释 |
|---|---|
| 单继承 | 一个子类只有一个父类,像亲子关系 |
| 多继承 | 一个子类有多个父类,注意 MRO 顺序 |
| 方法重写 | 子类定义与父类同名的方法,覆盖原行为 |
super() |
在子类中调用父类的方法,安全又方便 |
| 多态 | 同一操作,不同对象做出不同反应 |
| 鸭子类型 | 不关心类型,只关心有没有这个方法 |
| 类属性 | 属于类,所有对象共享一份数据 |
| 实例属性 | 属于具体对象,各管各的 |
| 类方法 | 用 @classmethod 和 cls,操作类数据 |
| 静态方法 | 用 @staticmethod,无 self/cls,就像类里的普通函数 |
| 私有成员 | 以 __ 开头,外部不能直接访问,需通过 get/set |
| 抽象类 | 不能实例化,强制子类实现指定方法 |
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)