详细总结Python类的多继承知识
我们知道python是一种面向对象的编程语言,他有对象这种基础概念,所以他也就有对象的三大属性:多态,继承和封装。在谈到继承的时候我们会了解到继承有两种形式,一种是单继承,一种是多继承,今天我们就来讲讲python类的多继承模式。
一、Python不同版本的类
- Python2.2之前是没有共同的祖先的,之后引入Object类,它是所有类的共同祖先类Object
- Python2中为了兼容,分为古典类(旧式类)和新式类
- Python3中全部都是新式类
- 新式类都是继承自Object的,新式类可以使用super
#古典类在python2.x中运行
class A: pass
print(dir(A)) # ['__doc__', '__module__']
print(A.__bases__) # ()
a = A()
print(a.__class__) # __main__.A
print(type(a)) # <type 'instance'>
新式类
#新式类在python3.x中运行
class B: pass
print(dir(B)) # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
print(B.__bases__) # (<class 'object'>,)
B = B()
print(B.__class__) # <class '__main__.B'>
print(type(B)) # <class '__main__.B'>
二、多继承
OCP原则:多用”继承“,少修改
继承的用途:增强基类,实现多态
多态
- 在面向对象中,父类、子类通过继承联系在一起,如果可以通过一套方法,就可以实现不同变现,就是多态
- 一个类继承自多个类就是多继承它将具有多个类的特征
三、多继承弊端
多继承很好的模拟了世界,因为事务很少单一继承,但是舍弃简单,必然引入复杂性,带来了冲突
如同一个孩子继承了来自父母双方的特征,那么到底眼睛像爸爸还是妈妈尼?孩子究竟改像谁多一点尼?
多继承的实现会导致编译器设计的复杂度增加,所以现在很多语言舍弃了类的多继承
C++支持多继承;Java舍弃了多继承
- Java中,一个类可以实现多个接口,一个接口也可以继承多个接口。Java的接口很纯粹,只是方法声明,继承者必须实现这些方法,就具有了这些能力,就能干什么
多继承可能会带来二义性,例如,猫和狗都继承自动物类,现在一个类多继承了猫和狗类,猫和狗都有了shout方法,子类究竟继承谁的shout尼?
- 解决方案:实现了多继承的语言,要解决二义性,深度优先或者广度优先
四、Python多继承实现
class ClassName(基类列表): 类体
多继承带来的路径选择问题,究竟继承那个父类的特征尼?
- 究竟先广度优先,还是深度优先
Python使用MRO(method resolution order) 解决类搜索顺序问题。
- 经典算法,按照定义从左到右,深度优先策略【比如Python2.2之前,左图的MRO算法,MyClass→D→B→A→C→A】
- 新式类算法,经典算法的升级,重复的只保留最后一个。【左图MRO是:MyClass→D→B→C→A→object】
- C3算法,在类被创建出来的时候,就计算除一个MRO有序列表。【Python3唯一支持的算法,左图中MRO是MyClass→D→B→C→A→object】C3过于复杂,没必要去记,我们只要记住【object.mro(),显示继承的方法,从左到右依次查找】
五、多继承的缺点
当类很多,继承复杂的情况下,继承路径太多,很难说清什么样的继承路径
团队协作开发,如果引入多继承,那代码将不可控
不管编程语言是否支持多继承,都应当避免多继承
Pythond的面向对象,我们看到太灵活,太开放,所以要团队守规矩
六、Mixin
类有下面的继承关系
文档Document类是其他所有文档类的抽象基类,Word、Pdf类是Document的子类
需求:为Document子类提供打印能力思路:
1、在Document中提供print方法
class Document:
def __init__(self,content):
self.coutent = content
def print(self):
print(self.coutent)
class Word(Document): pass
class Pdf(Document): pass
a = Word("tom com")
a.print() # tom com
基类提供的方法不因该具体实现,因为它未必适合子类的打印,子类中需要覆盖重写。
print算是一种能力 -- 打印功能,不是所有的Document的子类都需要的,所以,从这个角度出发有点问题
class Document:
def __init__(self,content):
self.coutent = content
def print(self):
print(self.coutent)
class Word(Document):
def print(self):
print("Word print {}".format(self.coutent))
class Pdf(Document): pass
a = Word("tom com")
a.print() # Word print tom com
思路二:需要打印的子类上增加
如果现有子类上直接增加,违反了OCP的原则,所以应该继承后增加
class Document: # 不允许修改
def __init__(self,content):
self.coutent = content
def print(self):
print(self.coutent)
class Word(Document): pass # 不允许修改
class Pdf(Document): pass # 不允许修改
class PrinttableWord(Word):
def print(self):
print("PrinttableWord print {}".format(self.coutent))
print(PrinttableWord.mro()) # [<class '__main__.PrinttableWord'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
a = PrinttableWord("tom com")
a.print() # PrinttableWord print tom com
看似不错,如果还要提供其他类似能力,如何继承?
应用于网络,文档应该具备序列化的能力,类上就应该实现序列化可序列化还可能分为使用pickle、josn、messagepack等
这个时候,发现,类又可能太多了,继承的方式不是很好了
功能太多,A类需要某几样功能,B类需要另外几样功能,很繁琐
思路三:装饰器,用装饰器增强一个类,把功能给类附加上去,那个类需要,就装饰它
def printable(cls):
def _print(self):
print("_print 装饰器 {}".format(self.coutent))
return _print
cls.print = _print
return cls
class Document:
def __init__(self,content):
self.coutent = content
def print(self):
print(self.coutent)
class Word(Document): pass
class Pdf(Document): pass
@printable
class PrinttableWord(Word): pass #先继承,后装饰
print(PrinttableWord.__dict__) # {'__module__': '__main__', '__doc__': None, 'print': <function printable.<locals>._print at 0x0173C228>}
a = PrinttableWord("tom")
a.print() # _print 装饰器 tom
优点:简单方便,在需要的地方动态增加
思路四:Mixin 【用类去继承】
先看代码
class PrintableMixin:
def print(self):
print("PrintableMixin {}".format(self.coutent))
class Document:
def __init__(self,content):
self.coutent = content
def print(self):
print(self.coutent)
class Word(Document): pass
class Pdf(Document): pass
class PrinttableWord(PrintableMixin,Word): pass
print(PrinttableWord.mro()) # [<class '__main__.PrinttableWord'>, <class '__main__.PrintableMixin'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
print(PrinttableWord.__dict__) # {'__module__': '__main__', '__doc__': None}
a = PrinttableWord("tom")
a.print() # PrintableMixin tom
Mixin就是其他类混合进来,同时带来了类的属性和方法
这里看来Mixin类和装饰器效果一样,也什么特别的,但是Mixin是类,就可以继承,增强功能
class PrintableMixin:
def print(self):
print("PrintableMixin {}".format(self.coutent))
class Document:
def __init__(self,content):
self.coutent = content
def print(self):
print(self.coutent)
class Word(Document): pass
class Pdf(Document): pass
class PrinttableWord(PrintableMixin,Word): pass
class SuperPrintableMixin(PrintableMixin,Word):
def print(self):
print("~"*30)
super(SuperPrintableMixin, self).print()
print("~"*30)
print(SuperPrintableMixin.mro()) # [<class '__main__.SuperPrintableMixin'>, <class '__main__.PrintableMixin'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
print(SuperPrintableMixin.__dict__) # {'__module__': '__main__', 'print': <function SuperPrintableMixin.print at 0x018264B0>, '__doc__': None}
a = SuperPrintableMixin("tom")
a.print() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# PrintableMixin tom
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
七、Mixin类
Minxin本质上就是多继承实现的
Mixin体现的是一种组合的设计模式
在面向对象的设计中,一个负载的类,往往需要很多功能,而这些功能有来自不同的类提供,这就需要很多的类组合在一起
从设计模式的角度来说,多组合,少继承。
- Mixin类的使用原则
- Mixin类中不应该显示的出现__init__初始化方法
- Mixin类通常不能独立工作,因为它是准备混入别的类中的部分功能实现
- Mixin类的祖先类也应该是Mixin类
- 使用时,Mixin类通常在继承列表第一个位置,例如:class PrintableWord(PrintableMixin,Word):pass
以上就是Python类的多继承知识的详细总结了,希望能给各位小伙伴一个参考,也希望大家以后多多支持W3Cschool!