说一下Python中的mixin模式

概述

mixin是一种设计模式,实际上它类似于模板编程或者说接口编程,首先定义好一个算法或者某个任务流程的执行框架,然后把具体细节实现在基类中,像C++Java则基本是将细节实现在派生类中。mixin的实现原理是,利用了Python的动态性,可以很方便地动态地改变类的继承关系,从而实现动态改变类的行为和属性。具体来说就是,通过修改某个类的__bases__属性(该属性不会出现在__dict__里面),实现了动态改变类的继承关系。

deallocator differs问题

关于这个问题详细的讨论可以参考考Assignment to bases of direct object subclasses,该问题讨论的版本是Python 3.6。在这里演示用的Python2.7.12版本中也是存在这个问题的,可以说一直到Python 3.6这个问题还依然存在。

这个问题在这种情况下回发生,当一个类直接由内置对象object派生时,对该类的__bases__进行赋值操作时将会报错,如:

1
2
3
class A(object): pass
class B(object): pass
A.__bases__ = (B,)

将会抛出一下异常信息:

1
2
3
4
5
6
TypeErrorTraceback (most recent call last)
in ()
1 class A(object): pass
2 class B(object): pass
----> 3 A.__bases__ = (B,)
TypeError: __bases__ assignment: 'B' deallocator differs from 'object'

mixin模式简单举例

按道理只要一个类不是直接继承自object(多继承这里就没去测试了),只要这种继承能够正常生成这个类的MRO列表,动态给这个类的__bases__赋值应该都会成功。下面举一个简单的例子说明mixin模式如何使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class OBJ(object):pass
class A(OBJ):
def todo(self):
self.set_value()
self.print_value()
class B(object):
def set_value(self):
print "B set_value"
class C(object):
def print_value(self):
print "C print_value"
a = A()
print "A __bases__:", A.__bases__
print "A MRO:", A.mro()
# a.todo() # 这里会出错error,找不到set_value函数
A.__bases__ = (B, C) # 这里动态改变了A的父类,使得其拥有了set_value和print_value两个函数
print "A __bases__:", A.__bases__
print "A MRO:", A.mro()
print "call todo:"
a.todo()

将会得到输出为:

1
2
3
4
5
6
7
A __bases__: (<class '__main__.OBJ'>,)
A MRO: [<class '__main__.A'>, <class '__main__.OBJ'>, <type 'object'>]
A __bases__: (<class '__main__.B'>, <class '__main__.C'>)
A MRO: [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <type 'object'>]
call todo:
B set_value
C print_value

可以看到,A的继承关系被动态改变了,在todo函数中写好要执行的框架,然后框架中用到的函数操作留给了未来的父类去实现,同时也不用把所有的函数都在一个父类实现,不同的父类实现不同的功能,然后拼到一起。

0%