Python|Python 技巧探究(上下文管理器和with语句)

Python|Python 技巧探究(上下文管理器和with语句)
文章图片
一:前言 Python 里面的 with 语句是被认为是晦涩难懂的特征之一,但是当你窥视它的内部你就会发现这里面并没有什么魔法。事实上它可以帮助我们写一些整洁和可读性高的代码。
那么 with 语句适合用于哪方面呢?它可以帮助我们简化一些常见的资源管理模式,允许它们被提取和重用。
二:案例探究 还是来看一个最常见的打开文件的例子吧!

with open('name.txt','w') as f: f.write('hello world')

使用 with 语句去打开文件的好处在于,不管文件是否正常打开,最后文件都会被正常的关闭。实际上前面的代码可以翻译为下面的方式:
f = open('name.txt','w') try: f.write('hello world') finally: f.clouse()

当然这样写看起来确实挺废话的,但是其实 try...finally 语法是很重要的。
如果不用这个语法,可能会这样写:
f = open('name.txt','w') f.write('hello world') f.clouse()

这样的写法看起来挺正常的,但是实际上它保证不了文件的正常关闭,如果文件没有正常关闭就会出现很多问题,所以使用 with 语句是很有必要的,它帮助我们正确的获取和释放资源。
with 语句的另一个好用的例子是在 Python 的标准库里的 threading.Lock
lock = threading.Lock# 普通的方式: lock.acquir() try: # do something finally: lock.release()# 更好的方式: with lock: # do something

通过这两个例子可以看到:使用 with 语句允许我们抽象更多的资源处理逻辑,取代每次显式的使用 try...finally 语句,方便我们写代码。舒服了哦!
with 语句可以使得处理系统资源的代码可读性更高,它帮助我们避免 bug 或者忘记释放资源。
三:让自己的项目支持with语句 现在其实觉得 open() 和 threading.Lock 没啥特殊或者魔法的。他们实际上都是可以使用 with 语句的。 我们也可以在类里面通过实现上下门管理器提供一些功能。
啥是上下文管理器( context manager) ?它是一个简单的接口,最基本的是我们写的这个类要实现 双下划线方法:__enter____exit__ ,Python 会在资源管理中适当的调用这两个方法。
让我们看看它是怎么实现 open() 上下文管理的:
class ManagedFile: def __init__(self, name) self.name = namedef __enter__(self): self.file = open(self.name, 'w') return self.filedef __exit__(self, exc_type, exc_val, exc_tb): if self.file: self.file.close()

这个 ManagedFile 类就实现了上下文管理器协议,那么就支持使用 with 语句,就像使用最开始的 open() 的例子那样:
with ManagedFile('name.txt') as f: f.write('hello Python')

当使用 with 语句进入上下文管理器时 Python 调用 __enter__方法,同时将获取到资源,当离开上下文管理器的时候 Python 调用 __exit__ 方法去释放资源。
像上面这样写一个基于上下文管理器的累不是在Python中支持 with 语句的唯一方法。 Python 标准库中的 contextlib 模块提供了一些基于上下文管理器的协议。
【Python|Python 技巧探究(上下文管理器和with语句)】例如我们可以使用 contextlib.contextmanager 装饰一个基于生成器的工厂函数,这样就可以使用 with 语句,来看看例子吧:
from contextlib import contextmanager@contextmanager def manager_file(name): try: f = open(self.name, 'w') yield f finally: f.close()

这样使用:
with manager_file('name.txt') as f: f.write('hello python')

在这个例子中 manager_file() 是一个生成器,在开始的时候获取到资源,然后暂时性的停止程序 yiled 这个资源给调用者。当调用者离开了上下文,这个生成器就会继续执行后续的代码,所以最后资源可以被释放给系统。
上面一个基于类的和基于生成器的方法去使用 with 语句是等价的,选一个可读性好的就行。
这里有一个问题就是,基于装饰器的实现方法要求开发者懂一些例如装饰器、生成器这样高级的 Python 语法概念。不过真的想学习 Python 这些高级语法也是要学习的。

    推荐阅读