当前位置: 首页 > news >正文

Python 继承的优缺点(处理多重继承)

处理多重继承

如 Alan Kay 所言,继承有很多用途,而多重继承增加了可选方案和复
杂度。使用多重继承容易得出令人费解和脆弱的设计。我们还没有完整
的理论,下面是避免把类图搅乱的一些建议。

把接口继承和实现继承区分开

使用多重继承时,一定要明确一开始为什么创建子类。主要原因可
能有:

  • 继承接口,创建子类型,实现“是什么”关系
  • 继承实现,通过重用避免代码重复

其实这两条经常同时出现,不过只要可能,一定要明确意图。通过
继承重用代码是实现细节,通常可以换用组合和委托模式。而接口
继承则是框架的支柱。

使用抽象基类显式表示接口

现代的 Python 中,如果类的作用是定义接口,应该明确把它定义为
抽象基类。Python 3.4 及以上的版本中,我们要创建 abc.ABC 或其
他抽象基类的子类(如果想支持较旧的 Python 版本,参见 11.7.1
节)。

通过混入重用代码

如果一个类的作用是为多个不相关的子类提供方法实现,从而实现
重用,但不体现“是什么”关系,应该把那个类明确地定义为混入类
(mixin class)。从概念上讲,混入不定义新类型,只是打包方
法,便于重用。混入类绝对不能实例化,而且具体类不能只继承混
入类。混入类应该提供某方面的特定行为,只实现少量关系非常紧
密的方法。

在名称中明确指明混入

因为在 Python 中没有把类声明为混入的正规方式,所以强烈推荐在
名称中加入 …Mixin 后缀。Tkinter 没有采纳这个建议,如果采纳
的话,XView 会变成 XViewMixin,Pack 会变成 PackMixin,图
12-3 中所有使用 «mixin» 标记的类都应该这么做。

抽象基类可以作为混入,反过来则不成立

抽象基类可以实现具体方法,因此也可以作为混入使用。不过,抽
象基类会定义类型,而混入做不到。此外,抽象基类可以作为其他
类的唯一基类,而混入决不能作为唯一的超类,除非继承另一个更
具体的混入——真实的代码很少这样做。

抽象基类有个局限是混入没有的:抽象基类中实现的具体方法只能
与抽象基类及其超类中的方法协作。这表明,抽象基类中的具体方
法只是一种便利措施,因为这些方法所做的一切,用户调用抽象基
类中的其他方法也能做到。

不要子类化多个具体类

具体类可以没有,或最多只有一个具体超类。 也就是说,具体类
的超类中除了这一个具体超类之外,其余的都是抽象基类或混入。
例如,在下述代码中,如果 Alpha 是具体类,那么 Beta 和 Gamma
必须是抽象基类或混入:

class MyConcreteClass(Alpha, Beta, Gamma):
"""这是一个具体类,可以实例化。"""
# ……更多代码……

为用户提供聚合类

如果抽象基类或混入的组合对客户代码非常有用,那就提供一个
类,使用易于理解的方式把它们结合起来。Grady Booch 把这种类
称为聚合类(aggregate class)。

例如,下面是 tkinter.Widget 类的完整代码
(https://hg.python.org/cpython/file/3.4/Lib/tkinter/init.py#l2141):

class Widget(BaseWidget, Pack, Place, Grid):
"""Internal class.
Base class for a widget which can be positioned with the
geometry managers Pack, Place or Grid."""
pass

Widget 类的定义体是空的,但是这个类提供了有用的服务:把四
个超类结合在一起,这样需要创建新小组件的用户无需记住全部混
入,也不用担心声明 class 语句时有没有遵守特定的顺序。Django
中的 ListView 类是更好的例子,稍后在 12.5 节讨论。

“优先使用对象组合,而不是类继承”

这句话引自《设计模式:可复用面向对象软件的基础》一书, 这
是我能提供的最佳建议。熟悉继承之后,就太容易过度使用它了。
出于对秩序的诉求,我们喜欢按整洁的层次结构放置物品,程序员
更是乐此不疲。

然而,优先使用组合能让设计更灵活。例如,对 tkinter.Widget
类来说,它可以不从全部几何管理器中继承方法,而是在小组件实
例中维护一个几何管理器引用,然后通过它调用方法。毕竟,小组
件“不是”几何管理器,但是可以通过委托使用相关的服务。这样,
我们可以放心添加新的几何管理器,不必担心会触动小组件类的层
次结构,也不必担心名称冲突。即便是单继承,这个原则也能提升
灵活性,因为子类化是一种紧耦合,而且较高的继承树容易倒。

组合和委托可以代替混入,把行为提供给不同的类,但是不能取代
接口继承去定义类型层次结构。

接下来,我们将从这些建议入手分析 Tkinter。

Tkinter好的、不好的和令人厌恶的方面

记住一点,自 1994 年发布的 Python 1.1 起,Tkinter 就在标准
库中了。Tkinter 的底层是 Tcl 语言优秀的 GUI 工具包 Tk。Tcl/Tk 组
合原本不是面向对象的,因此 Tk API 基本上就是一堆函数。尽管
没有使用面向对象方式实现,但是这个工具包的理念极具面向对象
思想。

前几节给出的建议 Tkinter 大都没有采用,不过第 7 点是个例外。但是
Tkinter 做得并不好,因为使用组合模式把几何管理器集成到 Widget 中
更好,如第 8 点所述。

tkinter.Widget 类的文档字符串开头说它是“内部类”。这或许表明
Widget 应该定义为抽象基类。Widget 自身虽然没有方法,但是它定义
了接口。它传达的意思是:“每个 Tkinter 小组件都会提供基本的方法
init、destroy,以及众多 Tk API 函数),此外还会提供三个
几何管理器中的全部方法。”你可以不同意这是定义接口的好方式(太
宽泛了),但是这样确实能定义接口,Widget 就把接口“定义”为超类
接口的联合。

封装 GUI 应用逻辑的 Tk 类继承自 Wm 和 Misc,这两个类既不是抽象
类,也不是混入(Wm 不算是混入,因为 TopLevel 的超类只有它一
个)。Misc 类的名称本身明显是代码异味。 Misc 有 100 多个方法,
而且所有小组件类都继承它。为什么每个小组件都要处理剪切板、文本
选择和计时器等?我们可能不能把文本粘贴到按钮上,也不能选择滚动
条里的文字。 Misc 应该拆分成几个专门的混入类,而且不是所有小组
件都应该继承这些混入。

说实在的,作为 Tkinter 的用户,你根本不用知道或使用多重继承。那
些都是隐藏起来的实现细节,你在自己的代码中只需实例化或子类化小
组件类。不过,如果你想查找自己需要的方法,在控制台中输入
dir(tkinter.Button),你会发现列出了 214 个属性, 此时你就是多
重继承的受害者。

除了这些问题,Tkinter 还是稳定而灵活的,未必那么不堪。陈旧(和默
认)的 Tk 小组件没有考虑现代的用户界面,但是 Python 3.1(2009 年发
布)提供了 tkinter.ttk 包,这个包提供的小组件很精美,外观同原
生的一样,开发出的 GUI 应用也更专业。此外,有些陈旧的小组件,如
Canvas 和 Text,功能异常强大。只需少量代码,就能把一个 Canvas
对象打造成简单的拖拽绘图应用。如果你对 GUI 编程感兴趣,Tkinter
和 Tcl/Tk 绝对值得一看。
然而,我们的主题不是 GUI 编程,而是多重继承的运用。显式使用混入
类的现代示例在 Django 中可以找到。

http://www.xdnf.cn/news/1040257.html

相关文章:

  • 25年股票交易半年小结~~
  • K8S 专栏 —— Pod 篇
  • visual studio学习250614(编译错误)
  • 速度与精度的结合:Faster R-CNN模型的性能剖析
  • 清晰了解序列化的来龙去脉
  • etcd基本数据库操作
  • 基于Python学习《Head First设计模式》第十三章 现实世界中的模式
  • c++中vector的使用
  • 前端开发中,实现多线程
  • 轮转数组题解
  • App跨平台技术2025年深度解析:核心原理与最佳实践
  • C语言环形数组(循环队列)详解:原理、实现与应用
  • BeckHoff <--> Festo Cmmt AS驱动器 EtherCat通讯
  • 1.16 Cookie 和 Session
  • 多商户商城+直播电商系统融合开发方案:一套源码搞定双场景应用
  • 解决vue3标签中引用动态图片失效问题
  • Python无限弹窗
  • CSS Margin纵向重叠(Margin Collapse)问题详解
  • springAI 大模型应用开发
  • 操作系统多级存储模型
  • 【AS32系列MCU调试教程】调试工具:Eclipse调试工具栏与窗口的深入分析
  • 《高等数学》(同济大学·第7版)第五章第一节定积分的概念与性质
  • 【多线程初阶】详解线程池(上)
  • 探险之物资储备c++
  • 多项目状态如何集中监控与汇总
  • uni-app项目实战笔记12--创建分类列表完成页面跳转
  • 解决在微信小程序中view组件下的text和images设置了样式display: flex; align-items: center;对不齐
  • layui在首页添加弹窗和跳转页面
  • Leetcode 398. 随机数索引
  • 设计师灵感仓库!IconViewer 右键一键提取系统图标,PNG 透明背景素材随取随用