一些闲聊:
Mixin是一种设计模式、设计思想
并不是某个特定的class或者函数.
Java中的Mixin叫interface
Ruby中的Mixin叫Module
[2]优点:
1.mixin设计迷失可以在不对类的内容的修改前提下,扩展类的功能(添加父类)
2.更加方便的组织和维护不同的组建
3.可以根据开发需要任意调整功能
4.可以避免产生更多的类
缺点:
受继承关系限制,推荐只有两层的继承使用。
#-------------------------------------------------
案例一[3]:
class Role:
def walk(self):
print('走')
#---------------------------------------------------------------
class RunMixin:
def run(self):
print('跑')
class PromptSkillMixin:
def use_prompt_skill(self):
print('使用了一个瞬发技能')
class WalkExMixin:#覆盖Role中的功能,因为下面是最先继承,Role是最后继承
def walk(self):
print('疾走')
class RoleEx(WalkExMixin, PromptSkillMixin, RunMixin, Role):
def marco(self):
return [self.run, self.use_prompt_skill]
def use_marco(self):
print("---------------use_marco技术----------------")
for action in self.marco():
action()
print("--------use_marco结束----------")
if __name__ == '__main__':
r = RoleEx()
r.use_marco()
r.walk()
实验结果:
---------------use_marco技术----------------
跑
使用了一个瞬发技能
--------use_marco结束------------------
疾走
这个案例显而易见的好处是:
你在不用修改Role的情况下扩展了很多功能.
因为有的时候,Role这个是由你的同事开发的,你没有修改的权限,你的同事仅仅给了你调用接口的权限,
或者说,为了免责和稳定,比如到时候出问题谁背锅啊,你不被公司允许修改原来的代码.
#------------------------------------------------
案例二[1]:
class Displayer():
def display(self, message):
print("--------②这里是Displayer.display函数---start-----")
print("Displayer的self=",self)
print(message)
print("--------②这里是Displayer.display函数---end-----\n")
class LoggerMixin():
def log(self, message, filename='logfile.txt'):
print("MySubClass self=",self)
with open(filename, 'a') as fh:
print("--------③这里是LoggerMixin.log函数---start-----")
fh.write(message)
print("--------③这里是LoggerMixin.log函数---end-----\n")
def display(self, message):
print("display的self=",self)#这里的self类指代的是MySubClass,并不是LoggerMixin
print("------①这里是LoggerMixin的display函数-start--\n")
super().display(message)#会调用Displayer.display()函数
self.log(message)
print("------①这里是LoggerMixin的display函数-end--\n")
class MySubClass(LoggerMixin, Displayer):
def log(self, message):
super().log(message, filename='subclasslog.txt')
subclass = MySubClass()
subclass.display("This string will be shown and logged in subclasslog.txt")
'''
1. MySubClass.display() is resolved to LoggerMixin.display().
MySubClass.display() 方法被解析为 LoggerMixin.display() 方法的调用。
这应该还是比较好理解的。因为对于 MySubClass 类来说,
在继承链上的两个父类,LoggerMixin 和 Displayer 来说,LoggerMixin 是最近的,因此调用它的 display() 方法。
2. LoggerMixin.display() calls super().display(), which is resolved to Displayer.display().
LoggerMixin.display() 方法调用了 super().display(),这一行代码按照我们刚才的解释,查看 MySubClass 的继承链,
是应该调用 Displayer 类的 display() 方法的。这一步是相对来说比较难以理解的。
让我们这么来理解它,当 LoggerMixin.display() 中调用了 super().display() 的时候,它会尝试去寻找属于当前类的继承链。而这个当前类是什么类呢?
不是 LoggerMixin 类,而是 MySubClass 类。MySubClass 类的继承连是 LoggerMixin,然后 Displayer。所以,我们就找到了 Displayer 的 display() 方法。
3. It alse calls self.log(). Since self, in this case,
is a MySubClass instance, it resolves to MySubClass.log().
MySubClass.log() calls super().log(), which is resolved back to LoggerMixin.log().
别忘了,我们的 LoggerMixin 类还调用了 self.log() 方法。
这个看似好像要直接调用 LoggerMixin 的 log 方法,其实不然。
LoggerMixin 的 display() 方法在当前语境中的 self,其实是 MySubClass 类的对象,
因此对于 MySubClass 类的对象,想要调用 log 方法,
是直接调用自己类中的 log 方法,也就是 MySubClass.log() 方法,而不是 LoggerMixin.log() 方法的。
而又因为 MySubClass.log() 方法调用了 super().log() 方法,这才根据继承链寻找最近的父类,才找到了 LoggerMixin 类中的 log() 方法进行调用。
这个例子在网上非常有名,[1]中分析了这个例子的细节,细节我已经都复制到上方的代码中了.
核心思想和目标是(个人分析):
假如没有LoggerMixin,这个代码就剩下Displayer,所以整体代码的目标是:
不修改Displayer的情况下(这个非常重要),扩充Displayer的功能.
#---------------案例二[1]实验结果-感性分析-------------------------
实验结果如下:
display的self= <__main__.MySubClass object at 0x7f59388331d0>
------①这里是LoggerMixin的display函数-start--
--------②这里是Displayer.display函数---start-----
Displayer的self= <__main__.MySubClass object at 0x7f59388331d0>
This string will be shown and logged in subclasslog.txt
--------②这里是Displayer.display函数---end-----
MySubClass self= <__main__.MySubClass object at 0x7f59388331d0>
--------③这里是LoggerMixin.log函数---start-----
--------③这里是LoggerMixin.log函数---end-----
------①这里是LoggerMixin的display函数-end--
我们很直观地可以看出:
如果没有LoggerMixin,那么上述结果就会只剩下红色部分,
当前实验结果的黑色部分是对原有的Displayer的类的功能进行扩充.
同时又确保Displayer的类不被修改
#----------------总结------------------------
个人总结MIXIN的作用:
不修改父类的情况下,对父类的功能进行扩增和删改
为什么没有"査"呢?像数据库那样的增删改查.
遗产项目中,同事给你的接口肯定是已经实现"查询和获取成员变量"的功能的,即使没有MIXIN也已经具备了"査"功能.
扩增:上面两个例子都体现了增加功能的作用
删改:参考上面的案例一,WalkExMixin中的Walk函数屏蔽了原始父类Role的Walk函数
所以说,MIXIN这种设计模式出现的目的就是:
可以对原始父类实现功能上的扩增和(删改).
#----------------------------------------
Reference: