今天继续面向对象的学习,主题是继承、多态、类成员等进阶知识。如果说第一天是学会“造一个独立的零件”,那么今天就是学会“把零件组装成家族,让它们互相协作”。


一、继承:为什么你的代码需要继承?

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,那么创建它的对象时就会报错。这保证了所有设备都有统一的接口,方便统一调用。


九、【全新综合案例】智能家居自动化中心

我们把今天学的所有知识整合起来,设计一个“智能家居自动化中心”。

需求

  1. 所有设备必须继承自 SmartDevice,并且强制实现 status_report 方法(抽象方法)。

  2. 用类属性统计设备总数。

  3. 每个设备有一个私有属性 _firmware_version,通过公共方法访问。

  4. 设备具有不同的“开机”行为(方法重写)。

  5. 编写一个自动化函数 auto_control,根据传入的设备列表,依次调用其 execute_command 方法(多态演示)。

  6. 定义一个 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
抽象类 不能实例化,强制子类实现指定方法
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐