返回介绍

11.11 本章小结

发布于 2024-02-05 21:59:47 字数 2718 浏览 0 评论 0 收藏 0

本章首先介绍了非正式接口(称为协议)的高度动态本性,然后讲解了抽象基类的静态接口声明,最后指出了抽象基类的动态特性:虚拟子类,以及使用 __subclasshook__ 方法动态识别子类。

我们首先回顾了 Python 社区对接口的惯常理解。在 Python 的历史中常常出现接口的身影,但它是非正式的,类似于 Smalltalk 的协议,而且在官方文档中,“foo 协议”“foo 接口”和“foo 类对象”这三种措辞是同一个意思。协议风格的接口与继承完全没有关系,实现同一个协议的各个类是相互独立的。在鸭子类型中,接口就是这样的。

通过示例 11-3,我们发现 Python 对序列协议的支持十分深入。如果一个类实现了 __getitem__ 方法,此外什么也没做,那么 Python 会设法迭代它,而且 in 运算符也随之可以使用。随后,我们继续编写第 1 章中的 FrenchDeck 示例,还动态添加了一个方法,从而让它支持洗牌。这里用到的是猴子补丁,突出了协议的动态本性。我们再一次见识到,部分实现协议也是有用的:添加可变序列协议中的 __setitem__ 方法之后,立即就能使用标准库中的 random.shuffle 函数。了解现有的协议能让我们充分利用 Python 丰富的标准库。

接下来,Alex Martelli 介绍了“白鹅类型”这个术语,18 以此描述一种新的 Python 编程风格。借助“白鹅类型”,可以使用抽象基类明确声明接口,而且类可以子类化抽象基类或使用抽象基类注册(无需在继承关系中确立静态的强链接),宣称它实现了某个接口。

18“白鹅类型”这种说法是 Alex 发明的,这是它第一次出现在书中。

FrenchDeck2 示例清楚地展示了显式继承抽象基类的优缺点。继承 abc.MutableSequence 后,必须实现 insert 和 __delitem__ 方法,而我们并不需要这两个方法。不过,即便是 Python 新手,只要查看 FrenchDeck2 类的源码,就能看出它是可变序列。此外,我们还得到一个额外好处,从 abc.MutableSequence 中继承了 11 个方法(其中五个间接继承自 abc.Sequence),而且拿来即用。

全面介绍图 11-3 中 collections.abc 模块里的各个抽象基类后,我们自己动手从头开始编写了一个抽象基类。PyMOTW.com(Python Module of the Week)网站的创建者 Doug Hellmann 道出了这么做的目的:

定义抽象基类之后,各个子类可以实现通用的 API。如果有人不熟悉应用程序的运作方式,却又想使用插件扩展,就可以利用这一功能……19

19PyMOTW 网站介绍 abc 模块的页面,“Why use Abstract Base Classes?”一节。

定义好 Tombola 抽象基类之后,我们创建了三个具体子类,两个继承 Tombola,另一个注册为虚拟子类——它们都能通过同一个测试组件。

本章结束之前,我们提到了几个内置类型是如何注册到 collections.abc 模块中的抽象基类的。这样,虽然 memoryview 没有继承 abc.Sequence,isinstance(memoryview, abc. Sequence) 的结果也是 True。最后,我们探究了 __subclasshook__ 魔法。这个方法的作用是让抽象基类识别没有注册为子类的类,你可以根据需要做简单的或者复杂的测试——标准库的做法只是检查方法名称。

最后的最后,我要重申 Alex Martelli 的警告:不要自己定义抽象基类,除非你要构建允许用户扩展的框架——然而大多数情况下并非如此。日常使用中,我们与抽象基类的联系应该是创建现有抽象基类的子类,或者使用现有的抽象基类注册。此外,我们可能还会在 isinstance 检查中使用抽象基类,但这比继承或注册更少见。需要自己从头编写新抽象基类的情况少之又少。

我使用 Python 15 年了,除了教学示例以外,我只在 Pingo 项目中编写过一个抽象类,即 Board(https://github.com/garoa/pingo/blob/master/pingo/board.py) 类。支持单板机和控制器的驱动是 Board 的子类,共用相同的接口。就算我把 pingo.Board 打造成抽象类,它也并没有继承 abc.ABC。20 我本打算把 Board 定义为抽象基类,但是 Pingo 项目有更重要的事情要做。

20ython 标准库也有这样做的,有些类虽然是抽象的,但是并没有显式地继承 abc.ABC。

本章适合使用下面这段话结尾:

尽管抽象基类使得类型检查变得更容易了,但不应该在程序中过度使用它。Python 的核心在于它是一门动态语言,它带来了极大的灵活性。如果处处都强制实行类型约束,那么会使代码变得更加复杂,而本不应该如此。我们应该拥抱 Python 的灵活性。21

——David Beazley 和 Brian Jones
《Python Cookbook(第 3 版)中文版》

21《Python Cookbook(第 3 版)中文版》第 281 页。

或者,像本书技术审校 Leonardo Rochael 所写的:“如果觉得自己想创建新的抽象基类,先试着通过常规的鸭子类型来解决问题。”

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文