关于Mixin设计模式

关于Mixin设计模式

  • 今天读到一篇文章,关于python的Mixin设计模式,文中提到动态加载类、动态加载函数等动态加载的概念,不是很理解,在度娘的指导下有了一些己见。遂抄起键盘做此记录,可能和真正的Mixin有偏差,甚至毫不相干......欢迎老师或同学帮忙解读一下,我确实想知道到底啥意思,干嘛用的。

Mixin

  • 是一种编程开发模式
  • 和Java的接口类似
    • 有一款冰淇淋,提供一些基础口味(主要功能类)有香草、巧克力味什么的,在这种基础口味上可以添加一些其他吃的,比如坚果、饼干(Mixin类)什么的
  • 本质是多继承实现的,体现的是一种组合的设计模式
  • 通常是实现了某种功能的类,用于被其他子类继承,将功能组合到子类中
  • 和传统的类继承由所不同,Mixin不能实例化,大多是在运行时动态的同其他类一起组合使用
  • 不明白的话没关系,我也不懂....我讲不明白😥

python的组合

  • 继承和组合
    • 继承:是类与类的关系,子类继承父类的属性,子类与父类是一种‘从属关系’
    • 组合:是对象和对象的关系,一个对象拥有另一个对象中的属性。
  • 比如A类和B类没有共同点,但是他们又有所关联,这样应该不能通过继承来实现。例如医院类和病患类,他们不能通过继承来实现什么,但是病患就是要到医院去看病,两个类就是有关联,怎么办呢,这就得使用组合了
  • 看不懂没关系,上才艺!👇
# 医院类和病患类demo
class hospital():
    '''
    address:医院地址
    patient:将病患对象传入这个形参中 
    '''
    def __init__(self,name,address,patient):
        self.name = name
        self.address = address
        self.patient = patient
    def fun(self):
        print("{}接收{}病患,现在状态为{}".format(self.name,self.patient.name,self.patient.status))
        if self.patient.status != "发烧":
            print("不是新冠,放行。进一步检查...")
        else:
            print("{}病患状态为{},被诊断为新冠,立即处死".format(self.patient.name,self.patient.status))
class disease():
    '''
    status:病患状态
    hospital:将医院对象传入这个形参中
    '''
    def __init__(self,name,age,sex,status,hospital):
        self.name = name
        self.age = age
        self.sex = sex
        self.status = status
        self.hospital = hospital
    def goTohospital(self):
        print("{0}今年{1}岁了,现在状态为{2},前往{3}看病".format(self.name,self.age,self.status,self.hospital.name))

# 实例化一个病患,并将医院对象传入病患对象中
'''
这里有个疑问,按照解释性编译应该会报hos未定义的错的,但是Jupyter没有报
我试了一下Python Shell编译确实报了hos未定义
个人认为是Jupyter使用了魔法或者什么咒语
'''
diseaser = disease("路人甲",18,"男","发烧",hos)
# 实例化一家医院,并将病患对象传入医院对象中
hos = hospital("人民医院","江苏省徐州市人民广场炸鸡店隔壁",diseaser)

# 病患去医院途中
diseaser.goTohospital()
# 医院诊断病患
hos.fun()
路人甲今年18岁了,现在状态为发烧,前往人民医院看病
人民医院接收路人甲病患,现在状态为发烧
路人甲病患状态为发烧,被诊断为新冠,立即处死

👆上面是在构造函数中初始化其他对象的,还可以通过下面这样的方式👇这样先把对象实例化,然后再赋值,就不会报未定义的错了,我试了一下,确实没报错。

obj1 = 对象实例化
obj2 = 对象实例化
obj1.obj2 = obj2
obj2.obj1 = obj1
这样就1对象中有2对象的属性,2对象中也有1对象中的属性了

调用函数格式如下👇
对象1.对象2.对象2的函数或属性
obj1.obj2.obj2Fun()
obj2.obj1.obj1Fun()

网上搜集来的关于Mixin类应该遵循的几个原则

  • Mixin实现的功能需要是通用且单一的,每个Mixin只实现一种功能
  • Mixin只用于子类功能的扩展,不能影响子类的主要功能
  • Mixin类自身不能进行实例化,仅用于被子类继承,将功能组合到子类中
  • 使用时Mixin类通常放在继承列表的第一个位置
  • 命名要加Mixin作为后缀
  • Mixin类的祖先类也应是Mixin类(但是我个人认为还是不要让Mixin类再去继承别的类了)
    • 这就好像带把手枪(主要功能类)去打劫,你想悄咪咪地开枪,就在枪口上加装消音器(Mixin类),你甚至还可以在消音器上贴粉红色小熊贴纸(另一个Mixin类)但是这样就显得你很可爱了。非必要,勿为之

Mixin类的目的

  • 为了给一个类增加更多的功能,在设计类的时候,我们要优先考虑通过多重继承来组合多个Mixin的功能,而不是设计多层次的复杂的继承关系
  • Mixin类主要用在不同的类有部分相同的接口的时候,使用Mixin不仅可以提高代码的复用性,还可以使相关操作集中在一个类中而不是分散在多个类中,提高了代码的可维护性

通过Mixin方式动态改变类的原有继承体系

  • 什么叫动态加载、动态继承
    • 动态加载技术最早用在Android项目中,目的是为了达到让用户不用重新安装APK就能升级应用的功能
    • 比如现在大部分的APP都有广告程序,早期应用商场在商家APP前要审核App有没有广告,开发者就先不在apk中写广告的代码,审核通过后,在用户运行app时再从服务器下载广告的代码,动态加载到APP中。
  • Mixin可以在不修改任何源代码的情况下,对已有类进行扩展,可以保证组件的划分,可以根据需要使用已有的功能进行组合,来实现“新”类,因为新的业务可能需要创建新的子类。

介绍两个关键字

  • __bases__:返回一个类的基类,元组类型
  • __mro__:返回类的方法解析顺序
# 列一:
class Fu():
    def fun1(self):
        print("I'm fun1 in Fu")
    def fun2(self):
        print("I'm fun2 in Fu")
        
class Zi1():
    def fun2(self):
        print("I'm fun2 in Zi1")
# Zi2继承Zi1
class Zi2(Zi1):
    def fun1(self):
        print("I'm fun1 in Zi2")

zi2 = Zi2()
zi2.fun1()
zi2.fun2()

print("-"*15)
# 打印Zi2类的继承关系,返回一个元组
print(Zi2.__bases__)

# 注意这一步,修改了Zi2类的继承关系
Zi2.__bases__ = (Fu,)+Zi2.__bases__
print(Zi2.__bases__)
print("-"*15)

zi2.fun1()
zi2.fun2()
I'm fun1 in Zi2
I'm fun2 in Zi1
---------------
(<class '__main__.Zi1'>,)
(<class '__main__.Fu'>, <class '__main__.Zi1'>)
---------------
I'm fun1 in Zi2
I'm fun2 in Fu

观察上面的代码,发现可以通过修改Zi2类的__bases__值实现动态修改一个类的继承关系。如上面Zi2.__bases__=(Fu,)修改为Zi2.__bases__=(Fu,Zi1)实例调用fun2函数就由调用Zi1类中的fun2()变为调用Fu类中的fun2()函数

这和MRO的顺序规则是一样的,在前面的类中的方法会覆盖后面类中与其相同名称的方法或属性

但是有一个隐患,如果改了类的继承关系,而这个类有多个实例,则这些实例都会被影响,所以可以通过修改实例的__dict__来动态加载函数

# 例二和例一差不多:
# 首先定义一个更改类继承关系的函数
def mixin(willChange_class,Mixin_class,flag = 0):
    if flag:
        # 将Mixin类动态添加到一个类的继承关系中,并列在继承顺序首位
        willChange_class.__bases__ = (Mixin_class,) + willChange_class.__bases__
    elif Mixin_class not in willChange_class.__bases__:
        # 与上一句意思差不多,只是类的继承顺序不一样,可看下面a的例子
        willChange_class.__bases__ += (Mixin_class,)
    else:
        print("无操作")
a = (1,)
a = (2,)+(1,)
print(a)
(2, 1)
a = (1,)
a += (2,)
print(a)
(1, 2)
class A():
    def fun1(self):
        print("在A类中的fun1函数")
class D_Mixin():
    def fun4(self):
        print("在D_Mixin类中的fun4函数")
class B(A):
    def fun2(self):
        print("在B类中的fun2函数")
class C(A):
    def fun3(self):
        print("在C类中的fun3函数")
        

# 先看一下C的继承关系
c_obj = C()
print(C.__bases__)
# 因为C类继承A类,可以调用A类中的fun1()方法
c_obj.fun1()
# 调用上面的mixin函数更改C类继承关系
mixin(C,D_Mixin,flag=1)
print(C.__bases__)  # 可以看见,C类优先继承了D_Mixin类
print(C.__mro__)    # 可以看见,C类的方法解析顺序是C -> D_Mixin -> A -> object

# 现在可以调用D_Mixin中的fun4()方法了
c_obj.fun4()
print("-"*30)

# 下面更改一下B类的继承顺序
# 先看一下B类的继承关系和方法解析顺序
print("B类继承关系更改前:")
print(B.__bases__)
print(B.__mro__)
# 调用mixin()方法更改继承顺序
mixin(B,D_Mixin)
print("B类继承关系更改后:")
print(B.__bases__)
print(B.__mro__)
# 可以发现B类的继承关系改为(A,D_Mixin)且D_Mixin类没有优先放在第一位,而是末尾添加
(<class '__main__.A'>,)
在A类中的fun1函数
(<class '__main__.D_Mixin'>, <class '__main__.A'>)
(<class '__main__.C'>, <class '__main__.D_Mixin'>, <class '__main__.A'>, <class 'object'>)
在D_Mixin类中的fun4函数
------------------------------
B类继承关系更改前:
(<class '__main__.A'>,)
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
B类继承关系更改后:
(<class '__main__.A'>, <class '__main__.D_Mixin'>)
(<class '__main__.B'>, <class '__main__.A'>, <class '__main__.D_Mixin'>, <class 'object'>)

例二去注释完整代码

def mixin(willChange_class,Mixin_class,flag = 0):
    if flag:
        willChange_class.__bases__ = (Mixin_class,) + willChange_class.__bases__
    elif Mixin_class not in willChange_class.__bases__:
        willChange_class.__bases__ += (Mixin_class,)
    else:
        print("无操作")
class A():
    def fun1(self):
        print("在A类中的fun1函数")
class D_Mixin():
    def fun4(self):
        print("在D_Mixin类中的fun4函数")
class B(A):
    def fun2(self):
        print("在B类中的fun2函数")
class C(A):
    def fun3(self):
        print("在C类中的fun3函数")
        
c_obj = C()
print(C.__bases__)
c_obj.fun1()
mixin(C,D_Mixin,flag=1)
print(C.__bases__)
print(C.__mro__)
c_obj.fun4()
print("-"*30)
print("B类继承关系更改前:")
print(B.__bases__)
print(B.__mro__)
mixin(B,D_Mixin)
print("B类继承关系更改后:")
print(B.__bases__)
print(B.__mro__)
posted @ 2021-02-03 23:20  CokeIsPower  阅读(158)  评论(2编辑  收藏  举报