5.8 总结
我们看了类定义中的3个特殊方法。call()方法用于创建一个可调用对象,可调用对象用于创建有状态的函数。之前的例子中定义了可以记忆之前计算结果的函数。
enter()和exit()函数用来创建上下文管理器,上下文用于处理with语句中的逻辑处理,之前的大多数例子包含了输入和输出。然而,在Python中,对于一些场景使用局部上下文处理起来很方便。接下来会介绍如何创建容器和集合。
5.8.1 可调用对象的设计要素和折中方案
在设计一个可调用对象时,需要考虑以下几点。
- 首先是API。如果一个对象使用函数式接口更好一些,那么使用可调用对象是合理的。通过使用collections.abc.Callable来确保可调用API被正确地创建,并且可以让读代码的人很明确地了解类的目的。
- 其次是函数的状态。Python中的普通函数没有迟滞性——没有保存的状态,而可调用对象可以保存状态,记忆化设计模式很好地应用了有状态的可调用对象。
可调用对象唯一的缺点就是需要的语法更多了。而一个普通函数的定义显得更简洁、出错概率小并且可读性更强。
把一个普通函数转换为可调用对象是很容易的,比如这个函数。
def x(args):
body
之前的函数可以被转换为下面的可调用对象。
class X(collections.abc.callable):
def call(self, args):
body
x= X()
在不破坏单元测试的情况下,以上的改动是最小的。而body中的代码可以直接在新的上下文中正常运行。
一旦完成修改,新功能就会被添加到可调用对象实现版本的函数中。
5.8.2 上下文管理器的设计要素和折中方案
上下文通常用于获取/释放、打开/关闭和加锁/解锁这类的操作对。大多数操作是与文件的I/O相关的,而且Python中的大多数文件操作对象已经是不错的上下文管理器了。
对于包含了多个处理步骤,而每步又包含了括号的逻辑来说,上下文管理器总是需要的。特别是对于最终需要调用close()方法的逻辑,应该包含在上下文管理器中。
Python中的一些类库提供了打开/关闭操作,可其中的对象并不是上下文对象。比如对于shelve模块,就没有创建一个上下文。
可以(而且应该)在操作shelve文件时使用contextllib.closing()上下文。我们会在第9章“序列化和保存——JSON、YAML、Pickle、CSV和XML”中对这一点进行介绍。
对于需要close()方法的类,可以使用closing()函数。对于生命周期中包含有获取/释放的类,可以在init()方法或类级别的open()方法中获取资源,然后在close()中释放。这样一来,类就可以和closing()函数很好地集成了。
以下是一个类封装的例子,内部包含了close()函数。
with contextlib.closing( MyClass() ) as my_object:
process( my_object )
contextllib.closing()函数会调用参数对象的close()方法。其中,my_object中包含了close()方法。
5.8.3 展望
在接下来的两章里,会介绍如何使用特殊函数创建容器和数字。在第6章“创建容器和集合”中,会详细介绍标准类库中的容器和集合,也会演示如何创建唯一的、新的容器类型。在第7章“创建数值类型”中,会介绍不同的数值类型以及如何创建自定义的数值类型。
