QQ登录

只需一步,快速开始

开启左侧

Python学习第五十六天—类和对象5-Mixin及案例源码剖析

[复制链接]
15271953841 发表于 2024-3-20 07:13:16 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
Mixin及案例源码剖析
一、Mixin:混入,乱入,其实它是一种设计模式,就是利用编程语言应有的特性,针对面向对象开发过程中反复出现的问题而设计出来的解决方案。

编辑模式下:
class Animal:                          (类)
    def __init__(self,name,age):           (构造函数)
        self.name = name
        self.age = age                    (需要传入名字和年龄)   

    def say(self):                          (这个类还有say的方法)
        print(f"我叫{self.name},今年{self.age}岁。")

        
class Pig(Animal):                            (再定义一个Pig类去继承Animal这个类)
    def special(self):
        print("我的技能是拱大白菜~")

p=Pig("大肠",5)                             (类Pig实例化一个对象,它的名字叫“大肠”,5岁,然后调用它的say方法和special方法)
p.say()
p.special()
保存,执行后的效果是
= RESTART: C:/Users/Administrator/AppData/Local/Programs/Python/Python312/pig.py
我叫大肠,今年5岁。
我的技能是拱大白菜~
现在由于剧情需要,要让大肠起飞,有没有办法,在不需要修改原有类的代码结构的前提下,让大肠,也就是Pig飞起来?
我们可以写一个类,它的功能就是起飞,然后让Pig类去继承它,那么继承一个类,它就能够使用这类里面的方法和属性。就像Pig类继承Animal的时候,就可以使用它的say方法,而且Pig在实例化的时候也使用了Animal的构造函数,这就叫继承。

编辑模式下对前面的代码进行修改为:
class Animal:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def say(self):
        print(f"我叫{self.name},今年{self.age}岁。")

class FlyMixin:
    def fly(self):
        print("哦豁,我还会飞~")

        
class Pig(FlyMixin,Animal):
    def special(self):
        print("我的技能是拱大白菜~")

p=Pig("大肠",5)   
p.say()
p.special()
p.fly()
保存,运行:
= RESTART: C:/Users/Administrator/AppData/Local/Programs/Python/Python312/pig.py
我叫大肠,今年5岁。
我的技能是拱大白菜~
哦豁,我还会飞~

这其实是游戏外挂的思路。
下面是在网上找到的一段稍有难度的代码,目的让大家对该技巧记忆深刻:(编辑模式下)
class Displayer:
    def display(self,message):
        print(message)

class LoggerMixin:
    def log(self,message,filename="logfile.txt"):
        with open(filename,"a") as f:
            f.write(message)
    def display(self,message):
        super().display(message)
        self.log(message)

class MySubClass(LoggerMixin,Displayer):
    def log(self,message):
        super().log(message,filename="subclasslog.txt")

subclass=MySubClass()                    (实例化一个对象subclass)
subclass.display("This is a test.")              (然后调用display方法,参数是This is a test.)
代码不多,也就拢共 22 行,22 行的代码里面,定义了 3 个类。其中 MySubClass 多继承了 LoggerMixin 类和 Displayer 类。看似并没有什么异常的代码里面,当你尝试去仔细推敲 subclass.display() 的调用逻辑之后,就变得异常的复杂。
问题:这个调用的display方法是谁的?
首先,不可能是自己的,类MySubClass并没有实现这个方法,我们要从父类里面查找,那么它的父类有两个类LoggerMixin和类Displayer,根据先左后右的原则,Python会去类LoggerMixin去查找display方法,果然就找到了,这里面这个display方法是带有message参数,也就是说,这里会把This is a test.传递给它。方法实现的第一句super().display,但super()会去父类里面去查找方法,那么这个类LoggerMixin的父类是谁,但没有写父类,那默认就是object,任何类都是object的子类,但是object作为一个总的基类,但不可能有这种display方法的。Super()函数是严重依赖MRO顺序的(上一节课学过的内容),我们不妨来看一下这个类MySubClass的MRO顺序是怎样的。
先把前面编辑的文件保存为demo.py,点击Run Module运行,它会打印
This is a test.
并同时在Python安装目录下生成一个subclasslog.txt文件,打开这个文件,This is a test.这句话被记录到该文件中,这是程序实现的逻辑。
再来看看MySubClass的MRO顺序:

MySubClass.mro()
      
[<class '__main__.MySubClass'>, <class '__main__.LoggerMixin'>, <class '__main__.Displayer'>, <class 'object'>]
所以在类LoggerMixin中调用super()函数,它会先去类Displayer中去查找,而不是object,这个super().display其实去调用类Displayer里面display方法,结果就是在屏幕上打印一行This is a test.这么一串字符串。
再看下一句:self.log(message),这个log方法是谁的,取决于self,  self.log吗,self其实subclass这个对象本身,这个前面几节课讲过,就是绑定吗,那么这个对象是来自于MySubClass,而类MySubClass里面刚好又有这个log方法的实现,所以调用的其实就是这个,然而MySubClass里面刚好又有这个log方法的实现,所以调用的其实就是这个,然而MySubClass里面又有一个super()函数,这玩意堪比过山车,一波未平一波又起,还是根据MRO顺序,
Object<——LoggerMixin,Displayer<——MySubClass
Python去LoggerMixin(先左后右的顺序)中找,它的功能就是创建一个文件,并将这个message追加保存文件中,文件名如果你有传递就用你的,如果没有,就用默认的logfile.txt,恰好,我们这里又指定了filename的参数的值为subclasslog.txt,所以文件名是subclasslog.txt.

如果上述的解释太过于难以理解,我们可以简单记住,self.method() 将会先在当前类中查看 method() 方法,如果没有,就在继承链中进行查找,查找顺序就是你继承的顺序从左到右,直到 method() 方法被找到。super().method() 与 self.method() 是差不多的,只是 super().method() 需要跳过当前类而已。

这也就是我们的 Mixin 类牵扯出来的多继承函数调用问题的展现。

话说回来,为什么要叫做 Mixin 类呢?
也就是说,我们的 LoggerMixin 类是无法单独使用的,它必须要和一个拥有 display() 函数定义的类一起混合使用。这也就是为什么它被称作是 Mixin 类的原因,它总是需要与其他类混合来加强其他类。

至于这种编写代码模式的作用,还是有很大作用的。可以大大简化和方便我们的代码的开发过程。

class MyMixin:
    def mixin_method(self):
        print("这是一个混入方法。")

class MyClass(MyMixin):
    def class_method(self):
        print("这是一个类方法。")
        self.mixin_method()  # 调用混入的方法

# 使用
my_instance = MyClass()
my_instance.class_method()
这个简单的例子展示了如何在Python中创建和使用一个混入(mixin)。MyMixin类定义了一个可以被其他类混入的方法mixin_method。MyClass继承了MyMixin,并定义了自己的方法class_method。在class_method中,我们调用了mixin_method,这是混入的方法。这个例子演示了如何在不使用类继承的情况下,向类中添加功能。

客服热线
400-1234-888 周一至周日:09:00 - 21:00
公司地址:襄阳市樊城区长虹路现代城5号楼188

创客帮MAKER.BAND青少年创客创意社区是一个融教育、科技、体育资讯为一体的综合服务平台,专注于教育创新、专注于科技体育、专注于教育资讯。

Powered by Discuz! X3.4 © 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表