大多数面向对象语言都不支持多重继承,因为这会导致著名的Diamond problem, 而 Python 虽然形式上支持多重继承,但其实现机制却是利用 mixin,从而有效 地避免了 Diamond problem。

github dengshuan博主

1.什么是mixin

Mixin 本意是混入,程序中用来将不同功能(functionality)组合起来,从而为 类提供多种特性。而虽然继承(inheritance)也可以实现多种功能,但继承一般 有从属关系,即子类通常是父类更加具体的类。而 mixin 则更多的是功能上的 组合,因而相当于是接口(带实现的接口)。

好比是联想电脑与电脑之间是继承关系,因而联想电脑具备电脑的各种功能;而 联想电脑与键盘之间则是 mixin 关系,同样也具备键盘的各种功能。

一般编程语言都不允许多重继承,主要是为了避免 diamond problem,即两个父 类如果有共同的祖父类,但对祖父类相同部分做了不同的修改,则这个类再继承 两个父类就会产生冲突。

ssl

类似于 git 版本控制中,如果两个人对同一段代码做了不同的修改,则合并时 就需要手动解决冲突。编程语言如果碰到 diamond problem 时依赖程序员决定 用哪个父类的特性,就会变得非常复杂而且容易产生歧义。

从上面分析可以看出其实单从功能上来说,完全可以用 mixin 取代继承,从而 可以不要类这个概念。最近几年新出的编程语言 Rust 和 Go 里面就没有类 (class)以及继承,但并不影响代码复用,它们也正是利用 mixin 这种机制实现 的代码复用,例如 Rust 中用特征(Trait)取代了类和接口。

两种观点其实是两种不同的世界观,目前类与继承的概念则更为流行,而且符合 人们对事物的认知:人们对白猫、黑猫、花猫观察后更容易抽象出猫的概念,而 不是将这些事物作为无规律的组合去看待。

2.Python中的mixin

理解了 mixin 概念之后,再将其运用到 Python 中,理解(形式上)多重继承 就会容易许多。python 对于 mixin 命名方式一般以 MixIn, able, ible 为后缀

由于 mixin 是组合,因而是做加法,为已有的类添加新功能,而不像继承一样 下一级会覆盖上一级相同的属性或方法,但在某些方面仍然表现得与继承一样, 例如类的实例也是每个 mixin 的实例。mixin 使用不当会导致类的命名空间污 染,所以要尽量避免 mixin 中定义相同方法,对于相同的方法,有时很难区分 实例到底使用的是哪个方法。

class Mixin1(object):
    def test(self):
        print("mixin 1")
    def which_test(self):
        self.test()

class Mixin2(object):
    def test(self):
        print("mixin 2")

class MyClass1(Mixin1, Mixin2):
    pass                        # 按从左到右顺序从 mixin 中获取功能并添加到 MyClass

class Myclass2(Mixin1, Mixin2):
    def test(self):             # 已有 test 方法,因而不会再添加 Mixin1, Mixin2 的 test 方法
        print("my class 2")

c1 = MyClass1()
c1.test()                       # => "mixin 1"
c2 = MyClass2()
c2.test()                       # => "my class 2"
c2.which_test()                 # => "my class 2"
isinstance(c1, Mixin1)          # => True
issubclass(MyClass1, Mixin2)    # => True

Mixin 强调的是功能而不像继承那样包括所有功能和数据域,但利用 mixin 同 样也可以实现代码复用,下面这段代码来自Stack Overflow,当然 functools.total_ordering() 装饰器已经提供相同功能了,这里仅用来说明 mixin 实现代码复用。

class Comparable(object):
    def __ne__(self, other):
        return not (self == other)

    def __lt__(self, other):
        return self <= other and (self != other)

    def __gt__(self, other):
        return not self <= other

    def __ge__(self, other):
        return self == other or self > other


class Integer(Comparable):
    def __init__(self, i):
        self.i = i


class Char(Comparable):
    def __init__(self, c):
        self.c = c

下面是 Python2 中动态加入 mixin 的方法[fn:1],python3 中已经不支持这种 方法了,python3 可能需要借助 type 等元编程工具实现[fn:2]动态 mixin

def MixIn(pyClass, mixInClass, makeLast=0):
    if mixInClass not in pyClass.__bases__:
        if makeLast:
            pyClass.__bases__ += (mixInClass,)
        else:
            pyClass.__bases__ = (mixInClass,) + pyClass.__bases

不过尽管动态 mixin 是可能的,但实际使用中要尽量避免这样做,因为可能会 使所有使用这个 mixin 的实例出现一些不可预知的问题。

python3.7 进阶

python 一切皆对象 python 魔法函数 python 类和对象 python Mixin python 自定义序列类 python dict python 对象引用、可变性和垃圾回收 python 元类编程