资讯详情

Python拾遗 上下文管理器和else语句 --流畅的python

本文的jupyter notebook这里有文件。

本文出处。

上下文管理器和else块

Python其他语言中有一些不常见的过程控制特征,因此往往被忽视。本章讨论了两个特征:with语句和上下文管理器

for, while和try语句的else子句

if以外的else语句

我们习惯于if/else但往往忽略语句,python中for, while, try语句也能跟else子句:for当循环运行完成时(没有break),才会运行else块

while当循环因为条件是假的而退出(不是break),才会运行else块

try当块中没有异常抛出时,才会运行else块

虽然这里使用的关键词是else,但实际使用then更符合它的语义:先做这个,再做那个。

其中try和else毕竟,联合使用有点令人费解else可以放置块代码try里面:如果有异常,不管是不是try块的剩余部分仍然是else一些代码不会执行。这里的优点是可以清楚地看到try哪些句子(哪些句子可能些句子可能会抛出预期异常),使逻辑更加清晰。

一个for/else的例子:

my_list = ['apple', 'juice']

for item in my_list:

if item == 'banana':

break

else:

print('No banna found!')

No banna found!

上下文管理器和with块

with句子的目的是简化try/finally模式,其中finally常用于释放重要资源。

with句子开始运行时,会在上下文管理器对象上调用__enter__方法;结束后调用__exit__方法。最常见的例子是关闭文件对象:

with open('mirror.py', 'w') as fp:

fp.write('emmmmm')

fp

fp.write('aha')

---------------------------------------------------------------------------

ValueError Traceback (most recent call last)

in ()

----> 1 fp.write('aha')

ValueError: I/O operation on closed file.

我们在这里看到fp是一个TextIOWarpper因为open函数返回该实例,该实例__enter__方法返回self。接着当with块退出时,会在上下文管理器对象上而不是__enter__返回的对象调用__exit__。

其中as子句是可选的。

以下是一个精心制作的例子来解释上下文管理器对象的上下文__enter__返回对象的差异。

class LookingGlass:

def __enter__(self): # <1>

import sys

self.original_write = sys.stdout.write # <2>

sys.stdout.write = self.reverse_write # <3>

return 'JABBERWOCKY' # <4>

def reverse_write(self, text): # <5>

self.original_write(text[::-1])

def __exit__(self, exc_type, exc_value, traceback): # <6>

import sys # <7>

sys.stdout.write = self.original_write # <8>

if exc_type is ZeroDivisionError: # <9>

print('Please DO NOT divide by zero!')

return True # <10>

with LookingGlass() as what:

print('Alice, Kitty and Snowdrop')

print(what)

pordwonS dna yttiK ,ecilA

YKCOWREBBAJ

what

'JABBERWOCKY'

神奇的看,在with块中打印的所有值都是相反的,当退出时with块后,打印恢复正常。

那么这个LookingGlass上下文管理器是怎么做到的?

回到上面的代码,我们发现现在__enter__在函数中,它交换了标准输出和逆转输出,然后返回了‘JABBERWOCKY'并绑定到what上。

那么在with块中,所有调用print(也就是sys.stdout.write)所有的句子都变成了调用reverse_write。

然后在退出的时候再做sys.stdout.write恢复正常。注意这里的exc_type, exc_value, trackback,当没有异常时,这里输入的参数都是None;否则是相关的异常数据。

contextlib模块中的实用工具

在定义上下文管理器之前,不妨看看能不能用。contextlib模块中的工具。closing

若对象提供close()方法,但没有实现__enter__/__exit__协议,那么可以 使用此函数构建上下文管理器。

suppress

构建临时忽略指定异常的上下文管理器。

@contextmanager

装饰器将简单的生成器函数转换为上下文管理器,因此无需创建管理器类别 协议了。

ContextDecorator

这是定义基于类的上下文管理器的基本类别。这种上下文管理器也可以用于装饰函数,在管理的上下文中运行整个函数。

ExitStack

上下文管理器可以进入多个上下文管理器。with块结束时,ExitStack按照后进 先出顺序调用栈内各上下文管理器__exit__方法。如果事先不知道。with块要进 可以使用多少个上下文管理器。例如,同时打开任何文件列表中的所有文件。

最常用的是@contextmanager,以下工具主要讨论如下。

使用@contextmanager

使用@contextmanager创建上下文管理器的样本代码可以减少,因为不再需要定义__enter__和__exit__,只需要实现一个有yield语句的生成器,由此产生想要的__enter__返回的值。

简单地说,以前__enter__协议的内容写在里面yield前面,__exit__协议的内容写在yield后面,yield本身生成__enter__的返回值。

前面的例子说明(省略异常处理模块):

import contextlib

@contextlib.contextmanager

def looking_glass():

import sys

original_write = sys.stdout.write

def reverse_write(text):

original_write(text[::-1])

sys.stdout.write = reverse_write

yield 'JABBERWOCKY'

sys.stdout.write = original_write

with LookingGlass() as what:

print('Alice, Kitty and Snowdrop')

print(what)

pordwonS dna yttiK ,ecilA

YKCOWREBBAJ

what

'JABBERWOCKY'

csv的inplace是一个使用@contextmanager构建上下文管理器的优秀用例,不妨去学习参考。

标签: banna变送器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

 锐单商城 - 一站式电子元器件采购平台  

 深圳锐单电子有限公司