python中的with语句及上下文管理器

with 语句是一种与异常处理相关的功能。with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源。

with语句的语法格式如下:

with context_expression [as target(s)]:
    with-body

在介绍with语句的工作原理之前,先来了解下上下文管理器。有了上下文管理器,with语句才能工作。

几个术语:

上下文管理协议(Context Management Protocol):支持该协议的对象要实现 __enter__() 和 __exit__()这两个方法。

上下文管理器(Context Manager):支持上下文管理协议的对象,这种对象实现了__enter__() 和 __exit__() 方法。通常使用 with 语句调用上下文管理器,也可以通过直接调用其方法来使用。

运行时上下文(runtime context):由上下文管理器创建,通过上下文管理器的 __enter__() 和__exit__() 方法实现,__enter__() 方法在语句体执行之前进入运行时上下文,__exit__() 在语句体执行完后从运行时上下文退出。

上下文表达式(Context Expression):with 语句中跟在关键字 with 之后的表达式,该表达式要返回一个上下文管理器对象。如果指定了 as 子句的话,会将上下文管理器的 __enter__() 方法的返回值赋值给 target(s)。target(s) 可以是单个变量,或者由“()”括起来的元组(不能是仅仅由“,”分隔的变量列表,必须加“()”)。

语句体(with-body):with 语句包裹起来的代码块,在执行语句体之前会调用上下文管理器的 __enter__() 方法,执行完语句体之后会执行 __exit__() 方法。


Python 的一些内建对象支持上下文管理器,可以用于 with 语句中,比如可以自动关闭文件、线程锁的自动获取和释放等。

with语句可以用try/finally语句替换,如下:

with语句:

with open(r'somefileName') as somefile:
    for line in somefile:
        print line
try/finally语句:

somefile = open(r'somefileName')
try:
    for line in somefile:
        print line
finally:
    somefile.close()

上下文管理器可以在开发中自定义,只需实现__enter__()和__exit__()两个方法。


为了对已有的生成器函数或者对象进行包装,加入对上下文管理协议的支持,避免了专门编写上下文管理器来支持 with 语句,可以用python模块contextlib提供的装饰器 contextmanager、函数 nested 和上下文管理器 closing来实现。

装饰器(contextmanager)

contextmanager 用于对生成器函数进行装饰,生成器函数被装饰以后,返回的是一个上下文管理器,其 __enter__() 和 __exit__() 方法由 contextmanager 负责提供。

语法:

@contextmanager
def some_generator(<arguments>):
    <setup>
    try:
        yield <value>
    finally:
        <cleanup>

with some_generator(<arguments>) as <variable>:
   <body>

实例:

from contextlib import contextmanager
 
@contextmanager
def demo():
    print '[Allocate resources]'
    print 'Code before yield-statement executes in __enter__'
    yield '*** contextmanager demo ***'
    print 'Code after yield-statement executes in __exit__'
    print '[Free resources]'
 
with demo() as value:
    print 'Assigned Value: %s' % value

输出:

  
  
[Allocate resources]
Code before yield-statement executes in __enter__
Assigned Value: *** contextmanager demo ***
Code after yield-statement executes in __exit__
[Free resources]

函数(nested)

nested 可以将多个上下文管理器组织在一起,避免使用嵌套 with 语句。

语法:

with nested(A(), B(), C()) as (X, Y, Z):
     # with-body code here

实例:

@contextmanager
def make_context(name):
  print 'entering:', name
  yield name
  print 'exiting:', name

with make_context('AA') as A, make_context('BB') as B, make_context('CC') as C:#同nested的用法
  print 'inside with statement:', A, B, C

with nested(make_context('AA'), make_context('BB'), make_context('CC')) as (A,B,C):
  print 'inside with statement:', A, B, C

输出:
entering: AA
entering: BB
entering: CC
inside with statement: AA BB CC
exiting: CC
exiting: BB
exiting: AA
entering: AA
entering: BB
entering: CC
inside with statement: AA BB CC
exiting: CC
exiting: BB
exiting: AA

上下文管理器(closing)

closing 上下文管理器包装起来的对象必须提供 close() 方法的定义,否则执行时会报 AttributeError 错误。closing 适用于提供了 close() 实现的对象,比如网络连接、数据库连接等,也可以在自定义类时通过接口 close() 来执行所需要的资源“清理”工作。

语法:

with closing(<module>.open(<arguments>)) as f:
            <block>

实例:

from contextlib import closing,nested,contextmanager

class ClosingDemo(object):
    def __init__(self):
        self.acquire()
    def acquire(self):
        print 'Acquire resources.'
    def free(self):
        print 'Clean up any resources acquired.'
    def close(self):
        self.free()
 
with closing(ClosingDemo()):
    print 'Using resources'

输出:
Acquire resources.
Using resources
Clean up any resources acquired.

本文部分摘自https://www.ibm.com/developerworks/cn/opensource/os-cn-pythonwith/,深表感谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值