一、引言

这篇博客,是我在看了《Python GUI Programming with Tkinter》第 76 页的小节 Exploiting the power of multiple inheritance 之后,压抑不住自己的激动兴奋之情,认真整理书写分享出来的。所用到的也是这一节作者写出来的代码,希望能给广大网友跟我一样的恍然大悟的痛快的感觉。

言归正传,Python 无疑是支持多继承的。我们可以利用 Python 的这种特性,实现一种叫做 Mixin 的类:

Mixin classes contain only a specific set of functionalities that we want to be able to mix in with other classes to compose a new class.(引用于 《Python GUI Programming with Tkinter》P76)

作者的这句话是什么意思呢?我大概翻译一下:

Mixin 类只包含了一组特定的函数集合,而我们将会将其与其他类进行混合,从而生成一个适用于实际需要的新类

看文本介绍太过于苍白,我们来看一个例子是最好的。

二、看一个 Mixin 类的实例

这里,我直接先上代码,有兴趣的同学,可以暂停到这里,看看这段代码中的 subclass.display() 这行代码,究竟是怎么执行的:

class Displayer():def display(self, message):print(message)class LoggerMixin():def log(self, message, filename='logfile.txt'):with open(filename, 'a') as fh:fh.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.display("This string will be shown and logged in subclasslog.txt")

代码不多,也就拢共 22 行,22 行的代码里面,定义了 3 个类。其中 MySubClass 多继承了 LoggerMixin 类和 Displayer 类。看似并没有什么异常的代码里面,当你尝试去仔细推敲 subclass.display() 的调用逻辑之后,就变得异常的复杂。

3 个类的作用

首先,我们先来依次介绍下这 3 个类的作用:

1. Displayer 类
只有一个函数,display 函数,其简单打印输出传进来的 message 字符串

2. LoggerMixin 类
这个类就是我们引言中提到的 Mixin 类,也就是说,这个类的方法,就是我们想要与 Displayer 类进行混合,然后生成我们想要的新类 MySubClass 的。

这个类有两个方法:

log 方法使用了 with open 语法操作了一个日志文件,日志文件缺省命名为 logfile.txt,然后将得到的 message 参数追加到这个文件中去。

display 方法调用了 super() 的 display 方法,然后调用了 self.log 方法。

这里问题就来了:我们的 Mixin 类根本就没有继承任何除了 Object 类之外的父类呀,也就是说,Object 类没有 display 方法,那么 LoggerMixin 类究竟是调用了谁的 display 方法呢?

这个问题相当诡异,我们之后再说。

3. MySubClass 类
这个类就是我们多继承而来的新类,只有一个方法,log 方法简单调用了父类的 log 方法。

提出问题

按照作者的原话来说:

Technically, LoggerMixin inherits from Python’s built-in object class, which has no display() method. How, then, can we call super().display() here?

这也是我在上一个标题中提出来的问题,我们的 LoggerMixin 类是怎么调用的 super().display() 方法的呢?

问题解释

作者很快就给出了答案:

In a multiple inheritance situation, super() does something a little more complex than just standing in for the superclass. It look up the chain of inheritance using something called the Method Resolution Order and determines the nearest class that defines the method we’re calling.

简单翻译下:

在多继承的环境下,super() 有相对来说更加复杂的含义。它会查看你的继承链,使用一种叫做 Methods Resolution Order(方法解析顺序) 的方式,来决定调用最近的继承父类的方法。

也就是说,我们的 MySubClass.display() 调用,触发了是这么一系列的行为:

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() 方法进行调用。

画图总结

为了方便大家理解,我画了一个简略的图方便理解:
一句简单的 subclass.display() 的背后,究竟发生了什么?

三、总结

看上面的解释或许还是觉得云里雾里,其实作者给出了更加简单的理解方式:

If this seems confusing, just remember that self.method() will look for method() in the current class first, then follow the list of inherited classes from left to right until the method is found. The super().method() will do the same, except that it skips the current class.

简单翻译下:

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

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

话说回来,为什么要叫做 Mixin 类呢?

Note that LoggerMixin is not usable on its own: it only works when combined with a class that has a display() method. This is why it’s a mixin class because it’s meant to be mixed in to enhance other classes.

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

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

阐述到这里,是不是也跟我一样有了恍然大悟的快感了呢?

Enjoy It:)

一个例子走近 Python 的 Mixin 类:利用 Python 多继承的魔力相关推荐

  1. python入门笔记——类和对象⑤(继承)

    python入门笔记--类和对象⑤(继承) # python中展现面向对象的三大特征:封装.继承.多态 # 封装:把内容封装到某个地方以便后续的使用 # 对封装来说,其实就是使用初始化构造方法将内容封 ...

  2. Python语言学习:利用python获取当前/上级/上上级目录路径(获取路径下的最后叶目录的文件名、合并两个不同路径下图片文件名等目录/路径案例、正确加载图片路径)之详细攻略

    Python语言学习:利用python获取当前/上级/上上级目录路径(获取路径下的最后叶目录的文件名.合并两个不同路径下图片文件名等目录/路径案例.正确加载图片路径)之详细攻略 目录 利用python ...

  3. Python语言学习:利用python语言实现调用内部命令(python调用Shell脚本)—命令提示符cmd的几种方法

    Python语言学习:利用python语言实现调用内部命令(python调用Shell脚本)-命令提示符cmd的几种方法 目录 利用python语言实现调用内部命令-命令提示符cmd的几种方法 T1. ...

  4. 用python做数据分析pdf_利用python进行数据分析pdf

    利用python进行数据分析pdf微盘下载!<利用python进行数据分析>利用Python实现数据密集型应用由浅入深帮助读者解决数据分析问题~适合刚刚接触Python的分析人员以及刚刚接 ...

  5. Python之ffmpeg:利用python编程基于ffmpeg将m4a格式音频文件转为mp3格式文件

    Python之ffmpeg:利用python编程基于ffmpeg将m4a格式音频文件转为mp3格式文件 目录 利用python编程基于ffmpeg将m4a格式音频文件转为mp3格式文件 1.先下载ff ...

  6. python提醒事件_利用python实现短信和电话提醒功能的例子

    有时候,我们需要程序帮我们自动检测某些事件的发生 这个需求是广泛存在的 因此,这里整理了利用python实现短信和电话提醒功能的方法 主要需要完成以下4个步骤: - 安装核心库:twilio - 注册 ...

  7. python秒表模块_利用python的kivy模块做一个秒表

    Kiy是一个开源的Python模块库,用于开发界面应用程序和其它采用自然用户界面的多点触控应用软件.它可以在Android,iOS,Linux,OS X和Windows执行,是一款有趣跨平台的模块库. ...

  8. python数据预测_利用Python编写一个数据预测工具

    利用Python编写一个数据预测工具 发布时间:2020-11-07 17:12:20 来源:亿速云 阅读:96 这篇文章运用简单易懂的例子给大家介绍利用Python编写一个数据预测工具,内容非常详细 ...

  9. python汇率转换_利用Python中的Xpath实现一个在线汇率转换器

    前言 在之前的语法里面,我们记得有一个初识Python之汇率转换篇,在那个程序里面我们发现可以运用一些基础的语法写一个汇率计算,但是学到后面的小伙伴就会发现这个小程序有一定的弊端. 首先,它不可以实时 ...

最新文章

  1. 操作系统实验4—磁盘调度
  2. 顺序查找与二分查找算法
  3. python成员运算符包括_Python 使用成员运算符案例
  4. elementui源码_Element UI 终于还是来啦
  5. windows下使用word2vec训练维基百科中文语料全攻略!(二)
  6. amd平台linux驱动安装失败,ati闭源驱动安装失败,问题解觉不了。
  7. [渝粤教育] 西南科技大学 理论力学 在线考试复习资料(1)
  8. TI DSP各种库文件
  9. linux远程利用漏洞CVE-2016-4484 93次空密码重试就可以获得root权限 影响大多数Linux版本...
  10. 软件设计学习笔记1_架构
  11. 【DeepExploit】基于强化学习的自动渗透工具
  12. 正则表达式(一) search
  13. 苹果6外音没有了怎么办_时尚高颜简约风,苹果蓝牙耳机AirPods2保护套精选
  14. 分享13道Redis面试题,助你面试不再慌
  15. 计算机网络中man是,在计算机网络中,MAN是城域网的简称。
  16. 单引号双引号等特殊字符插入mysql数据库失败
  17. Android相关面试题
  18. 网络存储与容灾系统-作业三
  19. 微信wed开发工具_微信web开发者工具64位
  20. [答疑]EA能否把时序图转为协作图

热门文章

  1. 用户运营体系中,用户精细化运营闭环是怎样的
  2. fcitx 中文切换设置
  3. 郑州大学“战疫杯”大学生程序设计在线邀请赛(1)题解(Python版本)
  4. 20道JavaScript经典面试题
  5. 深度学习——损失函数推导过程(三个方面诠释损失函数的由来意义)
  6. win10系统PhotoshopCC软件ps软件关闭开启时欢迎界面设置方法
  7. Linux Kernel in a Nutshell - 7
  8. Autofac 批量注入
  9. vgg16_reducedfc.pth
  10. Sympy简单教程(6)