# 第15章 上下文管理器和else块
"""
本章内容提要:
with语句和上下文管理器
for while try语句的else子句
"""
# 15.1 先做这个,再做那个:if语句之外的else块
# else子句的行为
"""
for / else
仅当for循环运行完毕(即没有被break终止)才运行else块
while / else
仅当while循环因为条件为false退出(即没有被break终止)才运行else块
try/ else
仅当try没有异常抛出时才运行else块,else子句抛出的异常不会由前面的except处理
在所有情况下,如果异常或者return\break\continue导致控制权跳到了符合语句的主块之外,
else子句也会被跳过.
其实这种情况从语义上来说使用then关键字更好,但是python没有then关键字,而且Guido讨厌添加新的关键字
"""
# 示例:
"""
for item in my_list:
if item.flavor == 'banana':
break
else:
raise ValueError('No banana flavor found')
"""
"""
try dangerous_call()
after_call()
except OSError:
log('OSError...')
"""
# 只有dangerous_call不抛出异常after_call()才会执行
# 为了清晰准确,try块中应该只放置只预期抛出语句异常的语句,因此像下面这样写 更好
"""
try dangerous_call()
except OSError:
log('OSError...')
else:
after_call()
"""
# python词汇表
"""
EAFP(easier to ask for forgiveness than permission)
取得原谅比获得许可容易
这是一种常见的python编程风格
,先假定存在有效的键或者属性,如果假定不成立,则捕获异常
所以代码中有很多的try...except块
LBYL(look before you leap)
三思而后行
这种编程风格在调用函数或者查找属性之前,先显式地测试前提条件
代码中有很多的if块
"""
# 15.2上下文管理器和with块
"""
上下文管理器对象存在的目的是管理with语句就像迭代器的存在是为了管理for语句一样
with语句的目的是简化try/finally模式
这种模式用于保证一段代码运行完毕以后,一定执行某一项操作
finally子句通常用于释放重要的资源
上下文管理器协议
包含__enter__和__exit__两个方法
with语句运行时,会在上下文管理器上调用__enter__方法
with语句运行结束时,会在上下文管理器调用__exit__方法
"""
# 15-1示例:把文件对象当成上下文管理器使用
"""
with open("mirror.py",encoding='utf-8') as fp: # ①
src = fp.read(60)
print(len(src))
print(repr(fp)) # <_io.TextIOWrapper name='mirror.py' mode='r' encoding='utf-8'>
print(fp.closed, fp.encoding) # True utf-8
fp.read(60) # Traceback
"""
#① 执行with语句后面的表达式得到的结果是上下文管理器对象
# 不过把值绑定到了目标变量上(as子句)是在上下文管理器上调用__enter__方法的结果
# open()函数返回TextIOWrapper类的实例,而改实例的__enter__方法返回self
# 不过__enter__方法除了返回上下文管理器对象之外,还可能返回其他对象
# 不管控制流程以那种方式退出with块,都会在上下文管理器对象上调用__exit__方法,
# 而不是在__enter__返回的对象上调用
# with语句的as子句是可选的,对open函数来说,必须加上as子句,以便获取文件的引用
# 不过,有些上下文管理器会返回None,因为没有有用的对象能提供给用户
# 示例:测试LookingGlass上下文管理器类
"""
from mirror import LookingGlass
with LookingGlass() as what: # ①
print("Alice,Kitty and Snowdrop") # ②
print(what)
print(what)
print('Back to normal')
# ① 上下文管理器是LookingGlass类的实例,上下文管理器上调用__enter__方法,把返回的结果绑定到what上
"""
# 示例15-4 在with块之外使用LookingGlass类
"""
from mirror import LookingGlass
manager = LookingGlass()
print(repr(manager))
monster = manager.__enter__()
print(monster == 'JABBERWOCKY')
print(monster)
print(manager)
manager.__exit__(None,None,None)
print(monster)
"""
# 15.3标准库contextlib中的实用工具
"""
closing
如果对象提供了close()方法,但没有实现__enter__和__exit__协议,
那么可以使用这个函数构建上下文管理器
suppress
构建临时忽略指定异常的上下文管理器
@contextmanager
这个装饰器把简单的生成器函数变成上下文管理器这样就不用类去实现管理器协议了
ContextDecorator
这是个基类,用于定义基于类的上下文管理器,这种上下文管理器也能用于装饰函数,
在受管理的上下文中运行整个函数
ExitStack
这个上下文管理器能够进入多个上下文管理器
with块结束的时候, ExitStack按照后进先出的顺序调用栈中各个上下文管理器的__exit__方法
如果事先不知道with块要进入多少个上下文管理器,可以实用这个类
例如,同时打开任意一个文件列表中的所有文件
"""
# 15.4使用@contextmanager
# 这个装饰器能够减少创建上下文管理器的样板代码
# 只需要一个yield语句生成想让__enter__方法返回的值
# yield把函数的定义体分割成两个部分,yield前面的代码在with语句开始的时候执行(即调用__enter__时)\
# yield后面的语句在with块结束的时候执行(即调用__exit__时)
# 示例15-5 使用生成器实现的上下文管理器
# mirror_gen.py
# 示例15-7:mirror_gen_exc.py 基于生成器的上下文管理器,实现了异常 处理
# mirror.py LookingGlass上下文管理器类
# mirror.py LookingGlass上下文管理器类
class LookingGlass:
def __enter__(self):
import sys
self.original_write = sys.stdout.write
sys.stdout.write = self.reverse_write
return 'JABBERWOCKY'
def reverse_write(self,text):
self.original_write(text[::-1])
def __exit__(self, exc_type, exc_val, exc_tb):
import sys
sys.stdout.write = self.original_write
if exc_type is ZeroDivisionError:
print('Please DO NOT divide by zero!')
return True
# __exit__的参数
# exc_type
# 异常类,例如ZeroDivisionError
# exc_val
# 异常实例,有时会有参数传给异常构造方法,例如错误消息,这些参数可以使用
# exc_val.args获取
# exc_tb
# traceback对象
mirror_gen.py
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
"""
实际上@contextlib.contextmanager会把函数包装成实现了__enter__和__exit__的方法的类
"""
if __name__ == '__main__':
with looking_glass() as what:
print("Alice,Kitty and Snowdrop")
print(what)
print(what)
# 示例15-7:mirror_gen_exc.py 基于生成器的上下文管理器,实现了异常处理
# 示例15-7:mirror_gen_exc.py 基于生成器的上下文管理器,实现了异常 处理
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
msg = ''
try:
yield 'JABBERWOCKY'
except ZeroDivisionError:
msg = 'Please DO NOT divide by 0!'
finally:
sys.stdout.write = original_write
if msg:
print(msg)
# 用于原地从写文件的上下文管理器
# 用于原地从写文件的上下文管理器
"""import csv
with inplace(csvfilename,'r',newline='') as (infh,outfh):
reader = csv.reader(infh)
writer = csv.write(outfh)
for row in reader:
row+=['new','columns']
writer.writerow(row)
"""
# inpalce函数是一个上下文管理器,为同一个文件提供了两个句柄
35岁学python,也不知道为了啥?