python 的多继承
Mixin

多继承


  • C++ 支持多继承,Java 舍弃了多继承
  • Java 中,一个类可以实现多个接口,一个接口也可以继承多个接口,详细可参看其他文档
  • 多继承可能会带来二义性,python 实现多继承,那就要解决二义性问题,深度优先或者广度优先

python 的多继承


Python 使用 MRO(method resolution order 方法解析顺序)解决基类搜索顺序问题,MRO 搜索算法采用 C3 算法(py2.3 之后),阻止二义性并解决了继承单调性,深度优先

class A:
    pass
class B(A):
    pass
class C(A):
    pass
class D(B):
    pass
class E(D,C): # 如果写成 class E(C,D),再观察mro,注意,这是C3算法的继承单调性
    pass

print(E.mro())
----------------------
[<class '__main__.E'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

Mixin


抽象类和抽象方法

  • 基类中只定义,不实现的方法称为抽象方法
  • 只要包含抽象方法的类称为抽象类
# 例1:区别于java等语言的抽象类和抽象方法
# python中抽象类仍可实例化
# python中子类的抽象方法仍可不实现
class Document: # 抽象类
    def __init__(self, content):
        self.content = content
    
    def print(self): # 抽象方法
        raise NotImplementedError('未实现')

class Word(Document):
    def print(self):
        print(self.content,'in word') # 类中的print和全局内建的print是否冲突呢? 

class Pdf(Document):
    def print(self):
        print(self.content,'in pdf') 

w = Word('test print string')
w.print()

# d = Document('test document string') # 可实例化,但抽象类不推荐去实例化
# d.print() # 抛异常

装饰器给类增加功能

  • 给类动态增加属性
  • 在需要的地方动态增加,更加灵活
# 例2: 只给需要的子类增加功能
class Document: 
    def __init__(self, content):
        self.content = content

# 为了给类动态增加属性/方法,在类外定义一个方法
def printable(cls):
    def _print(self): # 注意函数命名和全局内建print,区别例1
        print('{0} print in {1}'.format(self.content,self.__class__.__name__))
    cls.print = _print # 给类动态增加print方法
    return cls

@printable     # Word = printable(Word) # return Word
class Word(Document):
    pass

class Pdf(Document):
    pass

w = Word('test word string')
w.print()
------------------------------
test word string print in Word
# 不改动原代码
# 例3:
class Document: 
    def __init__(self, content):
        self.content = content
        
class Word(Document):
    pass

class Pdf(Document):
    pass

###################################################
def printable(cls):
    def _print(self): 
        print('{0} print in {1}'.format(self.content,self.__class__.__name__))
    cls.print = _print 
    return cls

@printable
class Note(Word):
    pass

n = Note('test note string')
n.print()
print(Note.__dict__) # 通过装饰器动态增加属性,修改了当前类,dict中增加了print
----------------------------------
test note string print in Note
{'__module__': '__main__', '__doc__': None, 'print': <function printable.<locals>._print at 0x000001C776FCF2F0>}

Mixin

  • Mixin 是通过多继承实现的
  • Mixin 体现的是一种组合的设计模式
  • 从设计模式的角度,多组合,少继承。也就是说,对于缺失的 A,B,C 功能,分别用 AMixin、BMixin、CMixin 来进行组合增强
  • Mixin 类通常放在继承列表的第一个位置
例4:
class Document: 
    def __init__(self, content):
        self.content = content
        
class Word(Document):
    pass

class Pdf(Document):
    pass

###################################################

class PrintableMixin(Word):
    def print(self): 
        print('{0} print in {1}'.format(self.content,self.__class__.__name__))

class Note(PrintableMixin,Word):
    pass

n = Note('test note string')
print(n.__class__.mro())
n.print() #print()方法按照MRO去查找,通过观察mro,通过多继承,加入mixin类,改变了mro,相当于增加了属性
print(Note.__dict__) # 通过mixin,未修改当前类
-------------------------------------------------------
[<class '__main__.Note'>, <class '__main__.PrintableMixin'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
test note string print in Note
{'__module__': '__main__', '__doc__': None}
  • Mixin 好处:Mixin 类是类,可以再次继承,对该 Mixin 类进行扩展
# 如:
class SuperPrintableMixin(PrintalbleMixin):
    # 之前增强
    print('之前增强')
    super().print()
    # 之后增强
    print('之后增强')

Mixin 遵循的使用原则

这些需要遵循的原则不是非要这么做,如果不遵循也不会有语法上的错误,只是建议遵循

  1. Mixin 类不要显式的出现__init__初始化方法,即其中仅包含需要补充的功能函数
  2. Mixin 类不要独立工作,他是准备混入 (mix in) 到其他类中的部分功能实现
  3. Mixin 的父类也应是 Mixin 类
  4. Mixin 类通常放在继承列表的第一个位置,如上例中class Note(PrintableMixin,Word):

参考


  • magedu