上下文管理器和else块
2018-01-19 本文已影响7人
一块大番薯
- for、while、try 语句的 else 子句
其实把 else 当做 then 更好,当 for、while、try 没有异常、return、break、continue语句导致控制权调到符合语句的主板之外时,then 才会执行。
不好的做法,after_call 不属于易产生异常的语句,不应该放进 try:
try:
dangerous_call()
after_call()
except OSError:
log('OSError...')
好的做法,当没有产生异常时才执行 after_call。虽然作用效果一样,但是这种做法更符合逻辑:
try:
dangerous_call()
except OSError:
log('OSError...')
else:
after_call()
EAFP:easier to ask for forgiveness than permission,获取原谅比获取许可容易。先干了再说。
先假定存在有效的键或属性,如果假定不成立再捕抓异常。代码中有很多 try 语句。
LBYL:leap before you leap,三思而后行。看准了才干。
事先检查。代码中有很多 if 语句
文件管理器和with块
文件管理器对象的存在是为了管理 with 语句,
迭代器的存在是为了管理 for 语句
文件管理器协议包含 _enter_ 和 _exit_ 两个方法。
- 区分上下文管理器和 _enter_ 方法返回的对象
# 为 sys.stdout.write 打猴子补丁,替换成自己编写的方法
import sys
class LookingGrass:
def __enter__(self):
self.original_write, sys.stdout.write = sys.stdout.write, self.reverse_write
return 'abcdefg'
def reverse_write(self, text):
return self.original_write(text[::-1])
def __exit__(self, exc_type, exc_value, traceback):
sys.stdout.write = self.original_write
上下文管理器
- LookingGrass() 返回上下文管理器,是一个 LookingGrass 实例
- as what 实质是上下文管理器调用 _enter_ 方法返回值绑定给 what
- 上下文管理器管理 with 语句,所以输出都是反转的
- 与函数和模块不同,with 块没有定义新的作用域,with 块外仍可以使用 what 变量
@contextmanager
把简单的生成器函数变成上下文管理器,这样不用创建类来实现上下文管理器协议
生成器函数用到 yield,此处却与迭代无关,但由此可以引出协程(执行到某处,暂停,让客户代码运行,直至客户让协程继续执行)
yield 生成原本 _enter_ 方法返回的值,yield 后面是原本 _exit_ 的代码
import sys
from contextlib import contextmanager
@contextmanager
def looking_glass():
orginal_write = sys.stdout.write
def reverse_write(text):
orginal_write(text[::-1])
sys.stdout.write = reverse_write
yield 'abcd'
sys.stdout.write = orginal_write
contextmanage
但是,这种做法不够妥当,因为:
如果 with 块中抛出了异常,Python 解释器将其捕获,在 yield 表达式中再将其抛出
但是,那里没有处理错误的代码,函数 looking_glass 中止,无法恢复 sys.stdout.write,正确做法:
import sys
from contextlib import contextmanager
@contextmanager
def looking_glass():
orginal_write = sys.stdout.write
def reverse_write(text):
orginal_write(text[::-1])
sys.stdout.write = reverse_write
msg = ''
try:
yield 'abcd'
except ZeroDivisionError:
msg = 'Please do not divide by zero'
finally:
sys.stdout.write = orginal_write
if msg:
print(msg)
杂谈(with 块与子程序的互补)
如果有一系列操作如: A-B-C、P-B-Q,有两条路:
- 把 B 拿出来,当作子程序
- 把 A 和 C、P 和 Q 拿出来,当作 with 块