继承
【一】概要
- 类继承是面向对象编程中的一种重要概念,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。继承使得子类能够重用父类的代码,同时可以在子类中添加新的方法或属性,或者重写父类的方法,以满足特定的需求。
- 在类继承中,子类继承了父类的属性和方法,这包括实例变量、类变量、以及父类中定义的方法。子类可以通过继承获得父类的特征,从而避免重复编写相似的代码。父类通常包含一般性的特征,而子类则可以根据需要添加、修改或覆盖这些特征。
【二】常用方法
| '''单继承''' |
| class Person(object): |
| eyes = 2 |
| |
| @staticmethod |
| def eat(): |
| print(f"需要吃饭") |
| |
| |
| class Chinese(Person): |
| skin = 'yellow' |
| |
| @staticmethod |
| def eat_with(): |
| print("吃饭用筷子") |
| |
| |
| c = Chinese() |
| print(c.eyes) |
| c.eat() |
| |
| |
| |
| |
| '''多继承''' |
| class ShangHai(Chinese, Person): |
| |
| language = '侬好' |
| |
| |
| s = ShangHai() |
| print(s.eyes) |
| s.eat_with() |
| print(s.language) |
| |
| |
| |
| |
【三】详解
【1】什么是继承
- 继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。
- 子类会“”遗传”父类的属性,从而解决代码重用问题(去掉冗余的代码)
【2】经典类和新式类
- 在Python2时,会有区分经典类和新式类的区别
- 经典类:没有显示的继承【基类object】的类,以及该类的子类
- 新式类:显示的声明继承【基类object】的类,以及该类的子类,都是新式类
- 在Python3中,统一都是新式类
- 在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
| class Earth: |
| pass |
| class Person(): |
| pass |
| |
| class Animal(object): |
| pass |
| |
| '''isinstance()可以判断第一个参数是否属于第二个参数类型''' |
| print(isinstance(Earth, object)) |
| print(isinstance(Person, object)) |
| print(isinstance(Animal, object)) |
【3】查看继承__base__
和__bases__
__base__
:查看父类
__bases__
:查看所有的父类
| class Earth(object): |
| pass |
| |
| class Person(object): |
| pass |
| |
| class Chinese(Person): |
| pass |
| |
| class ShangHai(Earth,Chinese): |
| pass |
| |
| |
| print(Person.__base__) |
| |
| print(ShangHai.__base__) |
| |
| print(ShangHai.__bases__) |
| |
【4】继承的重用性
- 类继承,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。继承使得子类能够重用父类的代码,同时可以在子类中添加新的方法或属性,或者重写父类的方法
| |
| class Father(object): |
| eyes = 2 |
| mouth = 1 |
| def run(self): |
| print("这是父类中的run函数") |
| |
| |
| class Son(Father): |
| print(f"子类继承父类中的属性。父子都有{Father.eyes} 只眼睛,{Father.mouth} 张嘴。") |
| |
| '''也可以重写父类中的方法''' |
| def run(self): |
| print("这是子类中的run函数") |
| def read(self): |
| print("这是子类中的read函数,是父类所没有的") |
| |
| |
| s = Son() |
| s.run() |
| s.read() |
| |
| Father.run(self=s) |
- 当我们想要定义一些类,这些类中都有一些相似的特征,我们就可以想到将这些特征抽象出来,为他们定义一个父类
【e.g.】我需要猫和狗两个类
| |
| class Cat(): |
| def eat(self): |
| print("猫可以吃东西") |
| def drink(self): |
| print("猫可以喝水") |
| def miaow(self): |
| print("猫会喵喵叫~") |
| |
| class Dog(): |
| def eat(self): |
| print("狗可以吃东西") |
| def drink(self): |
| print("狗可以喝水") |
| def bark(self): |
| print("狗会旺旺叫~") |
| |
| '''通过查看代码,我们会发现,猫和狗都会吃和喝,以及所有的动物都是会吃喝的,唯一的区别在于,猫是喵,狗是汪''' |
| '''于是,我们就可以根据相似的特征,定义一个父类Animal,将吃喝作为动物类共有的特征(属性)''' |
| |
| class Animal(object): |
| def eat(self): |
| print(f"{self.__class__.__name__}可以吃东西") |
| def drink(self): |
| print(f"{self.__class__.__name__}可以喝水") |
| |
| |
| class Cat(Animal): |
| def miaow(self): |
| print("猫会喵喵叫~") |
| |
| |
| class Dog(Animal): |
| def bark(self): |
| print("狗会旺旺叫~") |
| |
| |
| cat = Cat() |
| cat.eat() |
| cat.drink() |
| cat.miaow() |
| |
| |
| dog = Dog() |
| dog.eat() |
| dog.drink() |
| dog.bark() |
| |
| '''这样就可以减少代码冗余''' |
| class Hero(object): |
| |
| def __init__(self, name, attack, health): |
| self.name = name |
| self.attack = attack |
| self.health = health |
| |
| def hero_attack(self, enemy): |
| enemy.health -= self.attack |
| print(f"{self.name} 攻击了 {enemy.name} {self.attack} 滴血!") |
| |
| class Hero_one(Hero): |
| pass |
| class Hero_two(Hero): |
| pass |
| |
| |
| one = Hero_one("one", 10, 50) |
| two = Hero_two("two", 5, 75) |
| one.hero_attack(two) |
| two.hero_attack(one) |
【5】隐藏父类属性
- 父类如果不想让子类覆盖自己的方法,可以采用双下划线开头的方式将方法设置为私有的
| '''小练习:最后打印出的内容是什么?''' |
| class Foo: |
| def f1(self): |
| print('Foo.f1') |
| |
| def f2(self): |
| print('Foo.f2') |
| self.f1() |
| |
| |
| class Bar(Foo): |
| def f1(self): |
| print('Bar.f1') |
| |
| |
| |
| b = Bar() |
| b.f2() |
| class Foo: |
| |
| def f1(self): |
| print('Foo.f1') |
| def f2(self): |
| |
| print('Foo.f2') |
| self.f1() |
| |
| |
| |
| class Bar(Foo): |
| |
| def f1(self): |
| print('Bar.f1') |
| |
| |
| |
| b = Bar() |
| b.f2() |
| |
| |
- 如果父类,将属性隐藏起来,将属性私有化了,那么子类便无法调用属性了
| class Foo: |
| def __f1(self): |
| |
| print('Foo.f1') |
| |
| def f2(self): |
| print('Foo.f2') |
| self.f1() |
| |
| |
| class Bar(Foo): |
| def f1(self): |
| print('Bar.f1') |
| |
| |
| |
| b = Bar() |
| b.f2() |
【6】多继承
- python支持多继承,用逗号分隔开多个继承的类
class 类名(父类1,父类2,......)
,理论上不限制继承的父类数量,但一般建议不要继承那么多父类
| class A(): |
| '''代码块''' |
| pass |
| class B(): |
| '''代码块''' |
| pass |
| class C(): |
| '''代码块''' |
| pass |
| |
| class D(A,B,C): |
| pass |
【7】类属性查找顺序
- 类属性查找顺序,Python2 与 Python3 有所区别
- 在 Python 2 中,类属性的查找顺序是按照深度优先(Depth-First Search,DFS)来进行的。这意味着在多重继承的情况下,首先查找当前类,然后再递归地查找父类,一直沿着继承链向下查找,直到找到属性或者到达继承链的末端。
- Python 3 引入了 C3 线性化算法(C3 Linearization),该算法使用广度优先的方式来确定方法和属性的查找顺序,解决了多重继承中的一些问题,提供了更一致和可预测的属性查找顺序。在 Python 3 中,类属性的查找顺序更符合直觉,并且遵循 C3 线性化算法。
(1)Python2的查找顺序




(2)Python3的查找顺序
__mro__
:通过__mro__
方法来查看属性的查找顺序
__mro__
是一个类的元类顺序(Method Resolution Order,MRO)元组。MRO 定义了在多继承环境中,Python 解释器查找方法和属性的顺序。__mro__
元组显示了类的基类搜索顺序。
- 需要注意,当多重继承过于复杂,可能会触发TypeError
- 在 Python 中,MRO 是按照 C3 线性化算法来确定的,它确保了在多继承中的方法查找顺序。但是,在某些情况下,由于继承关系的复杂性,可能会导致无法创建一致的 MRO,从而引发
TypeError
。
| class A(object): |
| def test(self): |
| print('from A') |
| |
| |
| class B(A): |
| def test(self): |
| print('from B') |
| |
| |
| class C(A): |
| def test(self): |
| print('from C') |
| |
| |
| class D(B): |
| def test(self): |
| print('from D') |
| |
| |
| class E(C): |
| def test(self): |
| print('from E') |
| |
| |
| class F(D, E): |
| pass |
| |
| |
| f1 = F() |
| f1.test() |
| print(F.__mro__) |
| |
| |

| '''当继承类型并没有变成菱形时,将会按照深度优先查找至基类''' |
| |
| class A(object): |
| def test(self): |
| print('from A') |
| class Z(object): |
| def test(self): |
| print('from Z') |
| class B(Z): |
| def test(self): |
| print('from B') |
| class C(A): |
| def test(self): |
| print('from C') |
| class D(B): |
| def test(self): |
| print('from D') |
| class E(C): |
| def test(self): |
| print('from E') |
| class F(D, E): |
| pass |
| |
| f1 = F() |
| f1.test() |
| print(F.__mro__) |

在 C3 线性化规则中,属性查找的顺序如下:
- 深度优先: 首先按照继承树的深度进行查找,即首先查找当前类,然后是它的父类,再是父类的父类,以此类推。
- 从左到右: 在同一深度级别上,按照继承列表中的顺序进行查找。这是指在类定义时,继承的父类列表中,从左到右的顺序。
【8】Mixins机制:减少多继承分支
- 多继承的设计,常常被人诟病,因为又复杂,又容易产生属性重名等问题
- 所以引入了Minxins(混入)机制
(1)概念和特点
- Mixins 是一种设计模式,它是一种通过组合多个小而独立的类来实现代码重用和组件化的方法。Mixins 不是一种特定的技术栈,而是一种通用的编程概念。
- 在面向对象编程中,Mixins 通过将一些通用的功能模块化,然后通过多重继承的方式将它们组合到一个类中,从而实现了代码的重用和可组合性。Mixins 不独立存在,而是以一种松耦合的方式与其他类组合在一起,使得代码更加灵活和易于维护。
主要特点包括:
- 独立性低: Mixin 类通常不是独立的实体,它们不应该被单独实例化。它们旨在与其他类组合使用,提供额外的功能。
- 单一职责: 每个 Mixin 类应该具有清晰的、单一的功能,而不是试图实现太多不相关的功能。
- 代码复用: Mixins 通过多继承的方式将功能注入到其他类中,从而实现了代码的复用。
(2)举栗子
| class Vehicle(object): |
| pass |
| |
| class airplane(Vehicle): |
| def fly(self): |
| pass |
| class Helicopter(Vehicle): |
| def fly(self): |
| pass |
| class Car(Vehicle): |
| pass |
| class Vehicle(object): |
| pass |
| |
| class FlyMixins(): |
| def fly(self): |
| pass |
| |
| class Airplane(Vehicle, FlyMixins): |
| pass |
| class Helicopter(Vehicle, FlyMixins): |
| pass |
| class Car(Vehicle): |
| pass |
| |
| |
| airplane = Airplane() |
| airplane.fly() |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了