Python面向对象编程中的继承机制
- 继承的基本概念 =====
继承是面向对象编程的核心特性之一,它允许我们基于现有类创建新类,实现代码重用和层次化设计。在Python中,新创建的类可以继承一个或多个父类的属性和方法,这种机制被称为继承。被继承的类称为基类或超类,而继承这些基类的新类称为派生类或子类。
Python支持两种继承方式:单继承和多继承。
- 单继承与多继承的实现 ===============
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'>,)
- 经典类与新式类的区别 ==========
在Python 2中,经典类和新式类有明确区别,而在Python 3中,所有类都是新式类。
Python 2中,没有显式继承object类的被称为经典类;显式继承object类及其子类的被称为新式类。
Python 3中,所有类默认继承object类,因此都是新式类。object类是所有Python类的基类,提供了一些基础方法(如__str__)的实现。
- 属性查找机制 ==========
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 属性查找顺序总结
无隐藏属性时的查找顺序: 对象自身属性 → 创建对象的类 → 基类 → 更上层的基类
有隐藏属性时的查找顺序: 从当前执行方法的类中查找封装属性 → 找不到则报错
- 继承与抽象的关系 ========
抽象是从相似事物中提取共同特征的过程。例如:
- 足球运动员、篮球运动员 → 运动员
- 小轿车、公交车 → 车辆
- 台式机、笔记本电脑 → 计算机
继承则是基于抽象的结果,构建具体的类层次结构:
- 运动员 → 足球运动员、篮球运动员
- 车辆 → 小轿车、公交车
- 计算机 → 台式机、笔记本电脑
- 菱形继承问题与解决 =============
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'>)
- 派生类的实现 =====
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顺序查找基类方法。
- 组合的使用 =====
组合是指在类中,将其他类的对象作为数据属性使用,这是实现类之间关系的另一种重要方式。
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)
- 抽象类的应用 ======
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