Python--多继承(Mixin类)

本文介绍了Python不同版本的类,包括Python2的古典类和新式类、Python3的新式类。阐述了多继承的概念、用途及弊端,如带来冲突和二义性。还介绍了Python多继承的实现,通过MRO解决基类搜索顺序问题,重点讲解了C3算法。最后讲解了Mixin类,包括其本质、设计模式及使用原则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Python不同版本的类

  • Python2.2之前类是没有共同的祖先的,之后,引入object类,它是所有类的共同祖先类object。
  • Python2中为了兼容,分为古典类(旧式类)和新式类。
  • Python3中全部都是新式类。
  • 新式类都是继承自object的,新式类可以使用super。
# 以下代码在Python2.x中运行
# 古典类(旧式类)
class A: pass

# 新式类
class B(object): pass

print(dir(A))
print(dir(B))
print(A.__bases__)
print(B.__bases__)

# 古典类
a = A()
print(a.__class__)
print(type(a)) # <type 'instance'>

# 新式类
b = B()
print(b.__class__)
print(type(b))

多继承

  • OCP原则:多用“继承”、少修改
  • 继承的用途:在子类上实现对基类的增强、实现多态
  • 多态
  • 在面向对象中,父类、子类通过继承联系在一起,如果可以通过一套方法,就可以实现不同表现,就是多态。
  • 一个类继承自多个类就是多继承,它将具有多个类的特征。

多继承弊端

  • 多继承很好的模拟了世界,因为事物很少是单一继承,但是舍弃简单,必然引入复杂性,带来了冲突。
  • 多继承的实现会导致编译器设计的复杂度增加,所以有些高级编程语言舍弃了类的多继承。
  • C++支持多继承;Java舍弃了多继承。
  • Java中,一个类可以实现多个接口,一个接口也可以继承多个接口。Java的接口很纯粹,只是方法的声明,继承者必须实现这些方法,就具有了这些能力,就能干什么。
  • 多继承可能会带来二义性,实现多继承的语言,要解决二义性,深度优先或者广度优先。

Python多继承实现

在这里插入图片描述

  • 左图是多继承(菱形继承),右图是单一继承
  • 多继承带来路径选择问题
  • Python使用MRO(method resolution order方法解析顺序)解决基类搜索顺序问题。
  • 历史原因,MRO有三个搜索算法:
  1. 经典算法,按照定义从左到右,深度优先策略。2.2版本之前
    左图的MRO是MyClass,D,B,A,C,A
  2. 新式类算法,是经典算法的升级,深度优先,重复的只保留最后一个。2.2版本
    左图的MRO是MyClass,D,B,C,A,object
  3. C3算法,在类被创建出来的时候,就计算出一个MRO有序列表。2.3之后,Python3唯一支持的算法
    左图中的MRO是MyClass,D,B,C,A,object的列表

C3算法解决多继承的二义性
C3算法,解决了继承的单调性,它阻止创建之前版本产生二义性的代码。求得的MRO本质是为了线性化,且确定了顺序。
单调性:假设有A、B、C三个类,C的mro是[C, A, B],那么C的子类的mro中,A、B的顺序一致就是单调的。

Mixin类

  • Mixin本质上就是多继承实现的
  • Mixin体现的是一种组合的设计模式
  • 在面向对象的设计中,一个复杂的类,往往需要很多功能,而这些功能有来自不同的类提供,这就需要很多的类组合在一起
  • 从设计模式的角度来说,多组合,少继承

Mixin类的使用原则

  • Mixin类中不应该显式的出现__init__初始化方法
  • Mixin类通常不能独立工作,因为它是准备混入别的类中的部分功能实现
  • Mixin类的祖先类也应是Mixin类
  • 使用时,Mixin类通常在继承列表的第一个位置
  • 父类只做方法的定义
  • 抽象类,抽象方法
  • 其他语言抽象类不可实例化
  • 抽象类一般不实例化,作为基类用
class Document: # 第三方库,不允许修改
	def __init__(self, content):
		self.content = content

class Word(Document): pass # 第三方库,不允许修改
class Pdf(Document): pass # 第三方库,不允许修改

class PrintableMixin:
	def print(self):
		print(self.content, 'Mixin') 

class PrintableWord(PrintableMixin, Word): pass
print(PrintableWord.__dict__) # {'__module__': '__main__', '__doc__': None}
print(PrintableWord.mro())
# [<class '__main__.PrintableWord'>, <class '__main__.PrintableMixin'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]

pw = PrintableWord('test string')
pw.print() # test string Mixin

class SuperPrintableMixin(PrintableMixin):
    def print(self):
        print('-' * 20) # 打印增强
        super().print()
        print('-' * 20) # 打印增强

# PrintableMixin类的继承
class SuperprintablePdf(SuperPrintableMixin, Pdf): pass

print(SuperprintablePdf.__dict__) # {'__module__': '__main__', '__doc__': None}
print(SuperprintablePdf.mro())
# [<class '__main__.SuperprintablePdf'>, <class '__main__.SuperPrintableMixin'>,\
# <class '__main__.PrintableMixin'>, <class '__main__.Pdf'>, \
# <class '__main__.Document'>, <class 'object'>]

spp = SuperprintablePdf('super print pdf')
spp.print()
# --------------------
# super print pdf Mixin
# --------------------
### Python 多重继承的使用方法和查找顺序 #### 1. 多重继承的基础概念 在 Python 中,多重继承允许一个从多个基派生。这种特性使得子可以同时拥有多个父的方法和属性。然而,在实现过程中需要注意的是,Python 的方法解析顺序 (Method Resolution Order, MRO) 是通过 C3 算法来决定的。 C3 算法的核心原则可以用简单的一句话概括:“从一至顶,有子先出。” 这意味着从当前的第一个待查找父开始向上查找,当遇到某个父还有其他子时,且这些子也是当前的祖先之一,则优先查找该子,直到达到顶层 `object` 为止[^4]。 #### 2. 方法解析顺序 (MRO)Python 2.3 开始,所有的新式(即直接或间接继承自 `object` 的)都遵循 C3 算法来计算 MRO。这意味着即使存在复杂的继承结构,只要满足线性化条件,就可以唯一地确定调用链路[^1]。 下面是一个典型的多继承场景及其对应的 MRO 计算过程: ```python class A: pass class B(A): pass class C(A): pass class D(B, C): pass print(D.mro()) ``` 运行上述代码会输出如下结果: ``` [__main__.D, __main__.B, __main__.C, __main__.A, object] ``` 这里展示了如何应用 C3 线性化解析规则:首先列出最左侧父路径 (`D -> B -> A`) ,接着尝试加入右侧分支上的节点(`C`) 并保持一致性不破坏任何已存在的排列次序最终形成完整的序列[^5]。 #### 3. 使用 super() 函数处理多重继承 为了简化多重继承环境下的方法调用逻辑并避免重复执行同一个函数体内的操作,推荐采用内置函数 `super()` 来代替传统的显式指定父名称的方式。这种方式不仅更加优雅而且能够自动适配正确的 MRO 流程从而减少潜在错误风险[^3]。 例如考虑以下案例: ```python class BaseClass: def method(self): print('BaseClass') class Mixin1(BaseClass): def method(self): super().method() print('Mixin1') class Mixin2(BaseClass): def method(self): super().method() print('Mixin2') class Derived(Mixin1, Mixin2): def method(self): super().method() print('Derived') obj = Derived() obj.method() ``` 这段程序将依次打印出每一步的结果直至完成整个链条遍历,具体表现为: ``` BaseClass Mixin2 Mixin1 Derived ``` 这正是由于各层次间正确运用了超级引用机制配合既定好的MRO列表共同作用所致效果。 #### 4. 注意事项 尽管 Python 支持强大的多重继承功能,但在实际开发中应谨慎对待这一设计模式。因为随着项目规模增大以及维护周期延长,过于复杂的继承体系可能会带来难以理解和调试的问题。因此建议尽可能简化依赖关系或者改用组合(composition over inheritance)策略替代传统意义上的多层次嵌套定义形式。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值