python的else语句可以省略_Python拾遗 上下文管理器和else语句 --流畅的python

本文探讨Python中的for、while、try语句的else子句,以及上下文管理器的作用,特别是with语句如何简化try/finally模式。通过示例解释了上下文管理器的工作原理,并介绍了contextlib模块提供的实用工具,如@contextmanager。
摘要由CSDN通过智能技术生成

本文的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构建上下文管理器的优秀用例,不妨前去学习参考。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值