Python 高手编程系列三百二十:上下文管理器 — with 语句

为了确保即使在出现错误的情况下也能运行某些清理代码,try…finally 语句是
很有用的。这一语句有许多使用场景,例如:
• 关闭一个文件。
• 释放一个锁。
• 创建一个临时的代码补丁。
• 在特殊环境中运行受保护的代码。
with 语句为这些使用场景下的代码块包装提供了一种简单方法。即使该代码块引发了异常,你也可以在其执行前后调用一些代码。例如,处理文件通常采用这种方式:

hosts = open(‘/etc/hosts’)
try:
… for line in hosts:
… if line.startswith(‘#’):
… continue
… print(line.strip())
… finally:
… hosts.close()

127.0.0.1 localhost
255.255.255.255 broadcasthost
::1 localhost
利用 with 语句,上述代码可以重写为:
with open(‘/etc/hosts’) as hosts:
… for line in hosts:
… if line.startswith(‘#’):
… continue
… print(line.strip())

127.0.0.1 localhost
255.255.255.255 broadcasthost
::1 localhost
在前面的示例中,open 的作用是上下文管理器,确保即使出现异常也要在执行完 for
循环之后关闭文件。
与这条语句兼容的其他项目是来自 threading 模块的类:
• threading.Lock
• threading.RLock
• threading.Condition
• threading.Semaphore
• threading.BoundedSemaphore
一般语法和可能的实现
with 语句的一般语法的最简单形式如下:
with context_manager:

代码块


此外,如果上下文管理器提供了上下文变量,可以用 as 子句保存为局部变量:
with context_manager as context:

代码块


注意,多个上下文管理器可以同时使用,如下所示:
with A() as a, B() as b:

这种写法等价于嵌套使用,如下所示:
with A() as a:
with B() as b:

作为一个类
任何实现了上下文管理器协议(context manager protocol)的对象都可以用作上下文管
理器。该协议包含两个特殊方法。
enter(self):更多内容请访问https://docs.python.org/3.3/reference/datamodel.html
#object.enter
exit(self, exc_type, exc_value, traceback):更多内容请访问
https://docs.python.org/3.3/reference/datamodel.html#object.exit
简而言之,执行 with 语句的过程如下:
• 调用__enter__方法。任何返回值都会绑定到指定的 as 子句。
• 执行内部代码块。
• 调用__exit__方法。
__exit__接受代码块中出现错误时填入的 3 个参数。如果没有出现错误,那么这 3 个
参数都被设为 None。出现错误时,__exit__不应该重新引发这个错误,因为这是调用者
(caller)的责任。但它可以通过返回 True 来避免引发异常。这可用于实现一些特殊的使用
场景,例如下一节将会看到的 contextmanager 装饰器。但在大多数使用场景中,这一
方法的正确行为是执行类似于 finally 子句的一些清理工作,无论代码块中发生了什么,
它都不会返回任何内容。
下面是某个实现了这一协议的上下文管理器示例,以更好地说明其工作原理:
class ContextIllustration:
def enter(self):
print(‘entering context’)
def exit(self, exc_type, exc_value, traceback):
print(‘leaving context’)
if exc_type is None:
print(‘with no error’)
else:
print(‘with an error (%s)’ % exc_value)
没有引发异常时的运行结果如下:

with ContextIllustration():
… print(“inside”)

entering context
inside
leaving context
with no error
引发异常时的输出如下:
with ContextIllustration():
… raise RuntimeError(“raised within ‘with’”)

entering context
leaving context
with an error (raised within ‘with’)
Traceback (most recent call last):
File “”, line 2, in
RuntimeError: raised within ‘with’

  • 15
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值