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

Python面向对象编程中的继承机制

访客 技术 2026年7月16日 1
  1. 继承的基本概念 =====

继承是面向对象编程的核心特性之一,它允许我们基于现有类创建新类,实现代码重用和层次化设计。在Python中,新创建的类可以继承一个或多个父类的属性和方法,这种机制被称为继承。被继承的类称为基类或超类,而继承这些基类的新类称为派生类或子类。

Python支持两种继承方式:单继承和多继承。

  1. 单继承与多继承的实现 ===============

2.1 单继承的实现

单继承是指子类只继承一个基类的特性。下面是一个示例:

class Vehicle:
    """车辆基类"""
    type = '交通工具'

    def move(self):
        print('交通工具可以移动')

    def start(self):
        print('交通工具可以启动')


class Car(Vehicle):  # Car类继承自Vehicle类
    type = '汽车'

    def honk(self):
        print('汽车可以鸣笛')


my_car = Car()
my_car.move()  # 交通工具可以移动
print(my_car.type)  # 汽车   先在自身属性中查找

2.2 多继承的实现与继承关系查看

多继承是指子类可以同时继承多个基类的特性:

class Vehicle:
    """车辆基类"""
    type = '交通工具'

    def move(self):
        print('交通工具可以移动')

    def start(self):
        print('交通工具可以启动')


class Car(Vehicle):  # Car类继承自Vehicle类
    type = '汽车'

    def honk(self):
        print('汽车可以鸣笛')


class ElectricCar():
    def charge(self):
        print('电动车需要充电')


class Tesla(ElectricCar, Car):
    def autopilot(self):
        print('特斯拉支持自动驾驶功能')


my_tesla = Tesla()
my_tesla.move()  # 交通工具可以移动
print(my_tesla.type)  # 汽车   ElectricCar中没有type属性,在Car中找到
my_tesla.charge()  # 电动车需要充电   在ElectricCar中找到

# 使用__mro__方法查看继承顺序
print(Tesla.__mro__)  # (<class '__main__.Tesla'>, <class '__main__.ElectricCar'>, <class '__main__.Car'>, <class '__main__.Vehicle'>, <class 'object'>)

# 使用__bases__查看直接继承的基类
print(Tesla.__bases__)  # (<class '__main__.ElectricCar'>, <class '__main__.Car'>)

print(Vehicle.__bases__)  # (<class 'object'>,)
  1. 经典类与新式类的区别 ==========

在Python 2中,经典类和新式类有明确区别,而在Python 3中,所有类都是新式类。

Python 2中,没有显式继承object类的被称为经典类;显式继承object类及其子类的被称为新式类。

Python 3中,所有类默认继承object类,因此都是新式类。object类是所有Python类的基类,提供了一些基础方法(如__str__)的实现。

  1. 属性查找机制 ==========

4.1 无隐藏属性的查找

当存在继承关系时,对象在查找属性时会按照特定顺序进行:

class Professor:
    def teach(self):
        print('教授正在授课')

    def research(self):
        print('教授正在进行研究')
        # self本质上是调用该方法的对象
        # 该对象属于Student类,因此会查找Student类中的teach方法
        self.teach()


class Graduate(Professor):  # Graduate类继承自Professor类
    def teach(self):
        print('研究生正在助教')


grad1 = Graduate()  # 创建Graduate类的实例
grad1.research()  # 调用research方法
# 输出顺序将是'教授正在进行研究','研究生正在助教'
# 教授正在进行研究
# 研究生正在助教

4.2 有隐藏属性的查找

当属性被隐藏(名称修饰)时,查找机制会有所不同:

class Professor:
    def __teach(self):  # 私有方法,实际名称为_Professor__teach
        print('教授正在授课')

    def research(self):
        print('教授正在进行研究')
        # 实际调用grad1._Professor__teach()
        self.__teach()


class Graduate(Professor):
    def __teach(self):  # 私有方法,实际名称为_Graduate__teach
        print('研究生正在助教')


grad1 = Graduate()  # 创建Graduate类的实例
grad1.research()  # 调用research方法
# 输出将是'教授正在进行研究','教授正在授课'
# 因为Graduate类中没有_Professor__teach方法

4.3 属性查找顺序总结

无隐藏属性时的查找顺序: 对象自身属性 → 创建对象的类 → 基类 → 更上层的基类

有隐藏属性时的查找顺序: 从当前执行方法的类中查找封装属性 → 找不到则报错

  1. 继承与抽象的关系 ========

抽象是从相似事物中提取共同特征的过程。例如:

  • 足球运动员、篮球运动员 → 运动员
  • 小轿车、公交车 → 车辆
  • 台式机、笔记本电脑 → 计算机

继承则是基于抽象的结果,构建具体的类层次结构:

  • 运动员 → 足球运动员、篮球运动员
  • 车辆 → 小轿车、公交车
  • 计算机 → 台式机、笔记本电脑
  1. 菱形继承问题与解决 =============

6.1 菱形继承的概念

当多个类通过同一个基类继承,形成一个菱形的继承结构时,就产生了菱形继承问题。Python中解决此问题有两种方式:深度优先(经典类)和广度优先(新式类)。

6.2 菱形继承示例

class Device:
    def power_on(self):
        print('设备已开机')

class Phone(Device):
    def power_on(self):
        print('手机已开机')

class Computer(Device):
    def power_on(self):
        print('电脑已开机')

class Smartphone(Phone):
    def power_on(self):
        print('智能手机已开机')

class Tablet(Computer):
    def power_on(self):
        print('平板电脑已开机')

class SmartDevice(Smartphone, Tablet):
    pass

device = SmartDevice()
device.power_on()  # 输出: 智能手机已开机

6.3 经典类的深度优先查找

在经典类中,采用深度优先查找,先沿着一条继承路径找到最顶层基类,如果没找到再换另一条路径。

6.4 新式类的广度优先查找

在新式类中,采用广度优先查找,先在同一层级查找,找不到再查找更上层的基类。

6.5 查看类的继承顺序

可以使用__mro__方法查看类的解析顺序(Method Resolution Order),该方法仅对新式类有效:

class Device:
    def power_on(self):
        print('设备已开机')

class Phone(Device):
    def power_on(self):
        print('手机已开机')

class Computer(Device):
    def power_on(self):
        print('电脑已开机')

class Smartphone(Phone):
    def power_on(self):
        print('智能手机已开机')

class Tablet(Computer):
    def power_on(self):
        print('平板电脑已开机')

class SmartDevice(Smartphone, Tablet):
    pass

print(SmartDevice.__mro__)
# 输出: (<class '__main__.SmartDevice'>, <class '__main__.Smartphone'>, <class '__main__.Phone'>, <class '__main__.Tablet'>, <class '__main__.Computer'>, <class '__main__.Device'>, <class 'object'>)
  1. 派生类的实现 =====

7.1 派生的概念

派生是指子类在继承基类特性的基础上,添加自己的属性和方法,或者重写基类的属性和方法。

7.2 派生的基本实现

class Employee:
    company = '科技巨头'

    def __init__(self, name, position, salary):
        self.name = name
        self.position = position
        self.salary = salary

    def work(self):
        print('员工在工作')

class Developer(Employee):
    def __init__(self, programming_lang, name, position, salary):
        self.programming_lang = programming_lang
    def work(self):
        print('开发者正在编写代码')

dev1 = Developer(programming_lang='Python', name='张三', position='高级工程师', salary=20000)
print(dev1.programming_lang)  # Python
print(dev1.company)  # 科技巨头
dev1.work()  # 开发者正在编写代码

# 以下代码会报错,因为子类重写了__init__方法,导致基类的__init__方法失效
# print(dev1.position)  # AttributeError: 'Developer' object has no attribute 'position'

7.3 在派生类中继承基类属性的三种方法

方法一:直接调用基类的初始化方法

class Employee:
    company = '科技巨头'

    def __init__(self, name, position, salary):
        self.name = name
        self.position = position
        self.salary = salary

    def work(self):
        print('员工在工作')

class Developer(Employee):
    def __init__(self, programming_lang, name, position, salary):
        Employee.__init__(self, name, position, salary)  # 调用基类的初始化方法
        self.programming_lang = programming_lang

    def work(self):
        print('开发者正在编写代码')

dev1 = Developer(programming_lang='Python', name='张三', position='高级工程师', salary=20000)
print(dev1.programming_lang)  # Python
print(dev1.company)  # 科技巨头
print(dev1.position)  # 高级工程师

方法二:使用super()函数

super()函数返回一个临时对象,该对象允许我们调用父类的方法,并严格按照MRO规定的顺序进行查找:

class Employee:
    company = '科技巨头'

    def __init__(self, name, position, salary):
        self.name = name
        self.position = position
        self.salary = salary

    def work(self):
        print('员工在工作')

class Department:
    def __init__(self, department_name):
        self.department_name = department_name

    def meeting(self):
        print(f'在{self.department_name}部门开会')

class Developer(Department, Employee):
    def __init__(self, programming_lang, department_name, name, position, salary):
        super().__init__(department_name)  # super()只能继承第一个基类
        self.programming_lang = programming_lang

    def coding(self):
        super().work()  # 调用基类的方法

dev1 = Developer(programming_lang='Python', department_name='研发部', name='张三', position='高级工程师', salary=20000)
print(dev1.programming_lang)  # Python
print(dev1.department_name)  # 研发部
dev1.coding()  # 员工在工作

方法三:方法一与方法二的组合使用

class Employee:
    company = '科技巨头'

    def __init__(self, name, position, salary):
        self.name = name
        self.position = position
        self.salary = salary

    def work(self):
        print('员工在工作')

class Department:
    def __init__(self, department_name):
        self.department_name = department_name

    def meeting(self):
        print(f'在{self.department_name}部门开会')

class Developer(Department, Employee):
    def __init__(self, programming_lang, department_name, name, position, salary):
        Employee.__init__(self, name, position, salary)
        super().__init__(department_name)  # 继承第二个基类的属性
        self.programming_lang = programming_lang

    def coding(self):
        super().work()  # 调用基类的方法

dev1 = Developer(programming_lang='Python', department_name='研发部', name='张三', position='高级工程师', salary=20000)
print(dev1.programming_lang)  # Python
print(dev1.department_name)  # 研发部
print(dev1.name)  # 张三
print(dev1.position)  # 高级工程师
dev1.coding()  # 员工在工作

总结:

方法一需要显式传递self参数,方法二使用super()函数,它会自动处理self参数,并且按照MRO顺序查找基类方法。

  1. 组合的使用 =====

组合是指在类中,将其他类的对象作为数据属性使用,这是实现类之间关系的另一种重要方式。

class CPU:
    def __init__(self, brand, speed):
        self.brand = brand
        self.speed = speed

    def process(self):
        return f'{self.brand} CPU正在处理数据,速度为{self.speed}GHz'

class Memory:
    def __init__(self, capacity, type):
        self.capacity = capacity
        self.type = type

    def store(self):
        return f'{self.capacity}GB {self.type}内存正在存储数据'

class Storage:
    def __init__(self, capacity, type):
        self.capacity = capacity
        self.type = type

    def read(self):
        return f'{self.capacity}GB {self.type}硬盘正在读取数据'

class Computer:
    def __init__(self, cpu_brand, cpu_speed, mem_capacity, mem_type, storage_capacity, storage_type):
        self.cpu = CPU(cpu_brand, cpu_speed)
        self.memory = Memory(mem_capacity, mem_type)
        self.storage = Storage(storage_capacity, storage_type)
    
    def specs(self):
        return f'电脑配置: {self.cpu.process()}, {self.memory.store()}, {self.storage.read()}'

my_computer = Computer(cpu_brand='Intel', cpu_speed=3.5, mem_capacity=16, mem_type='DDR4', storage_capacity=512, storage_type='SSD')
print(my_computer.specs())  # 电脑配置: Intel CPU正在处理数据,速度为3.5GHz, 16GB DDR4内存正在存储数据, 512GB SSD硬盘正在读取数据

组合与继承的区别:

  • 继承表示"是一个"的关系(如汽车是交通工具)
  • 组合表示"有一个"的关系(如电脑有一个CPU)
  1. 抽象类的应用 ======

9.1 抽象类的概念

抽象类是一种特殊的类,它不能被实例化,只能被继承。抽象类通常包含一个或多个抽象方法,这些方法没有具体实现,需要子类来实现。

抽象类的作用是定义一组相关的类应该具有的共同接口,而不关心具体实现。例如,我们可以定义一个"形状"抽象类,然后创建具体的"圆形"、"矩形"等子类来实现具体的形状功能。

9.2 不实现抽象方法的后果

from abc import ABC, abstractmethod

class Shape(ABC):
    def __init__(self, name):
        self.name = name
    
    @abstractmethod
    def area(self):
        pass
    
    @abstractmethod
    def perimeter(self):
        pass

class Circle(Shape):
    def __init__(self, name, radius):
        super().__init__(name)
        self.radius = radius

# 尝试实例化Circle类会报错,因为它没有实现抽象方法
# circle = Circle('圆形', 5)
# TypeError: Can't instantiate abstract class Circle with abstract methods area, perimeter

9.3 正确实现抽象类方法

from abc import ABC, abstractmethod

class Shape(ABC):
    def __init__(self, name):
        self.name = name
    
    @abstractmethod
    def area(self):
        pass
    
    @abstractmethod
    def perimeter(self):
        pass

class Circle(Shape):
    def __init__(self, name, radius):
        super().__init__(name)
        self.radius = radius
    
    def area(self):
        return 3.14 * self.radius ** 2
    
    def perimeter(self):
        return 2 * 3.14 * self.radius

class Rectangle(Shape):
    def __init__(self, name, width, height):
        super().__init__(name)
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height
    
    def perimeter(self):
        return 2 * (self.width + self.height)

circle = Circle('圆形', 5)
print(f"{circle.name}的面积: {circle.area():.2f}")  # 圆形的面积: 78.50
print(f"{circle.name}的周长: {circle.perimeter():.2f}")  # 圆形的周长: 31.40

rectangle = Rectangle('矩形', 4, 6)
print(f"{rectangle.name}的面积: {rectangle.area()}")  # 矩形的面积: 24
print(f"{rectangle.name}的周长: {rectangle.perimeter()}")  # 矩形的周长: 20

相关文章

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...

发表评论

访客

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