python基础爬虫专题python自学

Python 技巧探究:上下文管理器和with语句

2018-09-14  本文已影响56人  梅花鹿数据rieuse

一:前言

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 = name

     def __enter__(self):
           self.file = open(self.name, 'w')
           return self.file

     def __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 模块提供了一些基于上下文管理器的协议。

例如我们可以使用 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 这些高级语法也是要学习的。

上一篇下一篇

猜你喜欢

热点阅读