深入理解Python之Context Manager

### 概述
本文主要讨论Python中的控制流特性

  • withcontext manager
  • elsefor,while,try中的使用

with表达式创建临时可靠的上下文。这能防止错误和减少模板代码,使API更便于使用和更安全。经常能够发现使用with表达式能够实现文件的自动关闭。

先做这个,然后做那个:if之后是else块

  • for: 循环执行完并且没有执行if
    1
    2
    3
    4
    5
    for item in my_list:
    if item.flavor == 'banana':
    break
    else:
    raise ValueError(‘No banana flavor found!')
  • while: 当循环条件变成false,循环退出执行
  • try: 当try块中没有抛出异常执行
    1
    2
    3
    4
    5
    6
    7
    try:
    dagerour_call()
    after_call()
    except OSError:
    log('OSError')
    else:
    after_call()

Context Manager和with

上下文管理器对象用于管理with表达式,就像迭代器用于管理for表达式

with表达式被设计用来简化try/finally模式,保证某些操作最后一定能够执行。

上下文管理器协议包含__enter____exit__方法.with开始,__enter__触发,而__exit__扮演了finally的角色,离开with块执行

1
2
with open('mirror.py') as fp:
src = fp.read(60)

上述代码打开文件,结束之后会自动关闭文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 Don\'t divide by zero!')
return True

contextib工具

  • closing: 创建对象上下文管理器,提供close方法,不需要实现__enter____exit__
  • suppress: 上下文管理器临时忽略指定的异常
  • @contextmanager: 直接生成上下文管理器,而不需要实现__enter____exit__
  • ContextDecorator: 上下文管理器装饰器基类
  • ExitStack: 能够进入几个上下文管理器。当with块结束,其会使用后进先出调用栈中的上下文管理器的__exit__

@contextmanager

@contextmanager减少创建上下文管理器的模板代码。使用@contextmanager时,yield会将函数分成两部分,之前的部分会在__enter__中执行,之后的部分会在__exit__中执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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
sys.stdout.write = original_write
msg = ''
try:
yield 'JABBERYWOCKY'
except ZeroDivisionError:
msg = 'Please DO NOT divide by zero'
finally:
sys.stdout.write = original_write
if msg:
print(msg)