当前位置:首页 > 技术 > 正文内容

Python 类的继承、多继承与派生

访客 技术 2026年6月4日 1

继承的概念与本质

在面向对象编程中,继承是一种机制,允许一个类(子类)获取另一个类(父类)的属性和方法。这实现了代码的重用和逻辑的组织。当一个子类继承父类时,它就拥有了父类的所有非私有成员,仿佛这些成员是它自己定义的一样。

  • 被继承的类称为父类(或基类、超类)。
  • 继承的类称为子类(或派生类)。

以下是一个简单的单继承示例。我们定义一个父类 Vehicle,它包含一些通用的属性和方法。然后,我们创建一个子类 Car,它继承自 Vehicle

class Vehicle:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model

    def start_engine(self):
        print(f"{self.brand} {self.model} 的引擎已启动。")

class Car(Vehicle):
    # Car 类继承了 Vehicle 的所有属性和方法
    pass

my_car = Car("Toyota", "Corolla")
print(my_car.brand)  # 输出: Toyota
my_car.start_engine()  # 输出: Toyota Corolla 的引擎已启动。

在这个例子中,Car 类是空的,但它能够访问 Vehicle 类的 brandmodel 属性和 start_engine 方法。

多继承

Python 支持多继承,这意味着一个子类可以同时继承多个父类。这使得类可以组合来自不同来源的功能。

例如,我们可以定义几个不同的类,每个类代表一种技能,然后创建一个继承所有这些技能的类。

class Engineer:
    def code(self):
        print("编写代码...")

class Musician:
    def play_instrument(self):
        print("弹奏乐器...")

class Artist:
    def paint(self):
        print("绘画...")

class CreativePerson(Engineer, Musician, Artist):
    # 这个类同时继承了 Engineer, Musician, 和 Artist 的能力
    pass

john = CreativePerson()
john.code()  # 输出: 编写代码...
john.play_instrument()  # 输出: 弹奏乐器...
john.paint()  # 输出: 绘画...

通过多继承,CreativePerson 类的对象 john 能够调用所有三个父类的方法。

名称查找顺序(MRO)

当访问一个属性或方法时,Python 会按照特定的顺序在类和对象中查找。这个顺序被称为方法解析顺序(MRO)。

单继承的查找顺序

在单继承中,查找顺序非常直接:先在对象自身查找,然后在其类中查找,最后在父类中查找。

class Parent:
    name = "父类"

class Child(Parent):
    name = "子类"

obj = Child()
obj.name = "对象自身"
print(obj.name)  # 输出: 对象自身
print(Child.name)  # 输出: 子类
print(Parent.name)  # 输出: 父类

多继承的查找顺序

多继承的查找顺序更为复杂,遵循 C3 线性化算法。对于菱形继承结构(多个类最终继承自同一个基类),Python 会采用广度优先的查找顺序。我们可以使用类的 mro() 方法来查看其方法解析顺序。

class G:
    name = "G"

class A(G):
    name = "A"

class B(G):
    name = "B"

class C(G):
    name = "C"

class D(A):
    name = "D"

class E(B):
    name = "E"

class F(C):
    name = "F"

class S(D, E, F):
    pass

print(S.mro())
# 输出: [<class '__main__.S'>, <class '__main__.D'>, <class '__main__.A'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.F'>, <class '__main__.C'>, <class '__main__.G'>, <class 'object'>]

这个顺序表明,当查找 S 类的属性时,Python 会先在 S 中查找,然后是 D,接着是 A,依此类推。

派生方法与 super 关键字

派生是指在子类中修改或扩展父类方法的行为。使用 super() 关键字可以方便地调用父类的方法。

例如,我们有一个 Device 基类,并创建一个 Phone 子类。我们想在子类的构造函数中调用父类的构造函数,并添加新的属性。

class Device:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model
        print("设备初始化完成。")

class Phone(Device):
    def __init__(self, brand, model, storage):
        super().__init__(brand, model)  # 调用父类的构造函数
        self.storage = storage
        print("手机初始化完成。")

my_phone = Phone("Apple", "iPhone 15", "256GB")
print(my_phone.__dict__)
# 输出: {'brand': 'Apple', 'model': 'iPhone 15', 'storage': '256GB'}

派生用法示例

我们可以通过派生来修改父类的行为。例如,我们可以创建一个自定义的字典类,限制其键必须是字符串。

class StrictDict(dict):
    def __setitem__(self, key, value):
        if not isinstance(key, str):
            raise TypeError("键必须是字符串")
        super().__setitem__(key, value)

my_dict = StrictDict()
my_dict["name"] = "Alice"  # 正常
print(my_dict)

try:
    my_dict[123] = "Bob"  # 会引发 TypeError
except TypeError as e:
    print(e)  # 输出: 键必须是字符串

派生方法实战:自定义 JSON 编码

Python 的 json 模块默认不支持序列化所有数据类型,例如 datetime 对象。我们可以通过继承 json.JSONEncoder 并重写其 default 方法来实现自定义的序列化逻辑。

import json
from datetime import datetime, date

class CustomJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        # 如果对象是 datetime 类型,则格式化为字符串
        if isinstance(obj, datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        # 如果对象是 date 类型,则格式化为字符串
        elif isinstance(obj, date):
            return obj.strftime('%Y-%m-%d')
        # 对于其他不支持的类型,调用父类的方法(会引发 TypeError)
        return super().default(obj)

data = {
    "event_time": datetime.now(),
    "event_date": date.today(),
    "message": "这是一个测试"
}

# 使用自定义的编码器进行序列化
json_str = json.dumps(data, cls=CustomJSONEncoder)
print(json_str)
# 输出类似: {"event_time": "2023-10-27 10:30:00", "event_date": "2023-10-27", "message": "这是一个测试"}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。