文章目录
- 1. else
- 2. with上下文管理器
- 3. contextlib模块实用工具
- 4. @contextmanager 装饰器
learn from 《流畅的python》
1. else
for/else、while/else 和 try/else
前两者 只有在 没有被break 时,才会运行 else
try 块中没有异常抛出时 才运行 else
for i in range(3):print(i)
else:print("finish, no break")# finish, no breakfor i in range(3):if i == 2:breakprint(i)
else:print("break")# 无输出i = 0
while i < 3:print(i)i += 1
else:print("no break") # no breaki = 0
while i < 3:if i==2:breakprint(i)i += 1
else:print("break") # 无输出arr = [0]
try:arr[0] = 1
except:print("error") # 无输出
else:print("no error, run else")# no error, run elsearr = []
try:arr[0] = 0
except:print("error") # error
else:print("error, will not run else") # 无输出
finally:print("finish") # finish
# finally 子句中的代 码通常用于释放重要的资源,或者还原临时变更的状态
else 这么用的比较少
2. with上下文管理器
上下文管理器协议包含 __enter__
和 __exit__
两个方法
with
语句 运行时,会在上下文管理器对象上调用__enter__
方法with
语句 结束后,会在上下文管理器对象上调用__exit__
方法,以此扮 演finally
子句的角色(释放重要的资源,或者 还原临时变更的状态),如 关闭文件等
class LookingGlass:def __enter__(self): # 没有其它的参数了import sysself.original_write = sys.stdout.writesys.stdout.write = self.reverse_writereturn 'ABCD'def reverse_write(self, text): # 反转字符串,实现输出self.original_write(text[::-1])def __exit__(self, exc_type, exc_value, traceback):# exc_type 异常类# exc_value 异常实例# traceback 对象# 在 try/finally 语句的 finally 块中调用 sys.exc_info()# 得到的就是 __exit__ 接收的这三个参数import syssys.stdout.write = self.original_writeif exc_type is ZeroDivisionError:print('Please DO NOT divide by zero!')return Truewith LookingGlass() as what:# 进入 __enter__ 函数时返回的 ABCD 字符串存入 whatprint("Michael learning python")# nohtyp gninrael leahciMprint(what)# DCBA
print("Michael learning python")
# Michael learning python
print(what)
# ABCD
manager = LookingGlass()
print(manager)
# <__main__.LookingGlass object at 0x00000231226F2190>
string = manager.__enter__()
print(string) # DCBA
print(string == "ABCD") # eurT, 所有的输出经过管理器的 反向输出处理了
print(string == "DCBA") # eslaF, 所有的输出经过管理器的 反向输出处理了
print(manager)
# >0912F62213200000x0 ta tcejbo ssalGgnikooL.__niam__<
manager.__exit__(None, None, None) # 还原了正常的输出
print(string) # ABCD
3. contextlib模块实用工具
closing
如果对象 提供了close()
方法,但没有实现__enter__/__exit__
协议,那么可以使用这个函数构建上下文管理器suppress
, 构建临时 忽略指定异常 的上下文管理器@contextmanager
,装饰器 把简单的 生成器函数 变成 上下文管理器,这样就不用创建类去实现管理器协议了ContextDecorator
,这是个基类,用于定义基于类的上下文管理器。这种上下文管理器 也能用于 装饰函数,在受管理的上下文中运行整个函数ExitStack
,这个上下文管理器 能进入多个 上下文管理器。with 块结束时,ExitStack
按照后进先出的顺序调用栈中各个上下文管理器的__exit__
方法。
如果事先不知道 with 块要进入 多少个上下文管理 器,可以使用这个类。例如,同时打开任意一个文件列表中的所有文件
使用最广泛的是 @contextmanager
装饰 器,因此要格外留心。
这个装饰器也有迷惑人的一面,因为它与迭代 无关,却要 使用 yield 语句
4. @contextmanager 装饰器
@contextmanager
装饰器能减少代码量,因为 不用编写一个完整的类,定义__enter__ 和 __exit__
方法,而 只需实现 有一个 yield 语句的生成器,生成想让__enter__
方法返回的值- 在使用
@contextmanager
装饰的生成器中,yield
语句的作用是 把函数的定义体分成两部分:
yield 语句前面的所有代码在with
块开始时 (即解释器调用__enter__
方法时)执行
yield 语句后面的代码在with
块结束时(即调用__exit__
方法时)执行
import contextlib@contextlib.contextmanager
def looking_glass1():import sysoriginal_write = sys.stdout.writedef reverse_write(text):original_write(text[::-1])sys.stdout.write = reverse_writeyield 'ABCD' # 产出的值绑定到 as 目标上# 执行 with 块代码时,函数会在这里暂停sys.stdout.write = original_write# 控制权一旦跳出 with 块,继续执行 yield 语句之后的代码with looking_glass1() as what:print("Michael learning python")print(what)
# nohtyp gninrael leahciM
# DCBA
print("Michael learning python")
print(what)
# Michael learning python
# ABCD
其实,contextlib.contextmanager
装饰器会把函数包装成实现 __enter__ 和 __exit__
方法的类
用于原地重写文件的上下文管理器
import csv
with inplace(csvfilename, 'r', newline='') as (infh, outfh): reader = csv.reader(infh) writer = csv.writer(outfh) for row in reader: row += ['new', 'columns'] writer.writerow(row)
inplace
函数是个上下文管理器,为同一个文件提供了两个句柄(例中的 infh 和 outfh),以便同时读写同一个文件。比标准库中的 fileinput.input
函数 易用