with语句支持在一个叫上下文管理器的对象的控制下执行一系列语句,语法大概如下:

with context as var:
statements

其中的context必须是个上下文管理器,它实现了两个方法__enter__,__exit__。

1.需求是怎么产生的

在正常的管理各种系统资源(文件、锁定和连接),在涉及到异常时通常是个棘手的问题。异常很可能导致控制流跳过负责释放关键资源的语句。

看一段简单的文件写入代码:

filename = 'my_file.txt'
f = open(filename,'w')
f.write('Hello ')
f.write('World')
f.close()

如果发生了意外的情况,例如写入'World'时磁盘空间不足,就会抛出异常,那么close()语句将不会被执行。

一般的解决方案是使用try-finally语句:

try:
filename = 'my_file.txt'
f = open(filename,'w')
f.write('Hello ')
f.write('World')
finally:
f.close()

但随着语句的增多,try-finally显然不够简洁,用with-as语句可以很简洁的实现以上功能:

with open('my_file','w') as f:
f.write('Hello ')
f.write('World')

这样不仅能处理出现异常的情况,而且还避免了在open一个文件后忘记了写close()方法的情况。

线程中的锁其实也实现了上下文管理协议:

import threading
with threading.Lock():
# 关键部分
statements
# 关键部分结束

再也不怕忘记将锁释放了。

2.上下文管理器的实现

上下文管理器要实现__enter__和__exit__的特殊方法。

__enter__(self): 进入上下文管理器时调用此方法,其返回值将被放入with-as语句中as说明符指定的变量中。

__exit__(self,type,value,tb):离开上下文管理器调用此方法。如果有异常出现,type、value、tb分别为异常的类型、值和追踪信息。如果没有异常,

3个参数均设为None。此方法返回值为True或者False,分别指示被引发的异常得到了还是没有得到处理。如果返回False,引发的异常会被传递出上下文。

文件上下文管理协议大概是如下实现的:

class OpenFile(object):
def __init__(self,filename,mode):
self.filename=filename
self.mode=mode
def __enter__(self):
self.f=open(self.filename,self.mode)
return self.f #作为as说明符指定的变量的值
def __exit__(self,type,value,tb):
self.f.close()
return False #异常会被传递出上下文
with OpenFile('my_file.txt','w') as f:
f.write('Hello ')
f.write('World')

  相当于

try :
执行__enter__的内容
finally:
执行__exit__的内容

3.异常的处理 

__exit__函数就能够拿到关于异常的所有信息(异常类型,异常值以及异常追踪信息),这些信息将帮助异常处理操作。

class ListTrans(object):
def __init__(self,alist):
self.alist=alist
def __enter__(self):
self.thecopy=list(self.alist)
return self.thecopy
def __exit__(self,exc_type,value,tb):
if exc_type is None:
self.alist[:]=self.thecopy
return False

没有异常发生时:

alist=[]
with ListTrans(alist) as working:
working.append(1)
working.append(2)
print alist

生成:

[1, 2]

有异常发生时:

alist=[]
with ListTrans(alist) as working:
working.append(1)
working.append(2)
raise RuntimeError('we are hosed')
print alist

生成:

RuntimeError: we are hosed

alist无变化。

可以捕捉异常:

alist=[]
try:
with ListTrans(alist) as working:
working.append(1)
working.append(2)
raise RuntimeError('we are hosed')
except RuntimeError as e:
print e
print alist

生成:

we are hosed
[]

当然,也可以简单的将__exit__的返回值设为True来忽略异常。

4.contextmanager装饰器

@contextmanager

contextlib模块的contextmanager装饰器可以更方便的实现上下文管理器。

任何能够被yield关键词分割成两部分的函数,都能够通过装饰器装饰的上下文管理器来实现。任何在yield之前的内容都可以看做在代码块执行前的操作,

而任何yield之后的操作都可以放在exit函数中。

from contextlib import contextmanager
@contextmanager
def listTrans(alist):
thecopy=list(alist)
yield thecopy
alist[:]=thecopy
alist=[]
with listTrans(alist) as working:
working.append(1)
working.append(2)
print alist

yield返回的值相当于__enter__的返回值。

要注意的是,这不是异常安全的写法,也就是说,当出现异常时,yield后的语句是不会执行的,想要异常安全,可用try捕捉异常:

from contextlib import contextmanager
@contextmanager
def listTrans(alist):
thecopy=list(alist)
try:
yield thecopy
except RuntimeError:
pass
alist[:]=thecopy
alist=[]
with listTrans(alist) as working:
working.append(1)
working.append(2)
raise RuntimeError

nested与closing

contextlib模块还有两个好玩的方法:nested,closing。

nested:用来更方便的减少嵌套写法:

当要嵌套的写上下文管理器时:

with open('toReadFile', 'r') as reader:
with open('toWriteFile', 'w') as writer:
writer.writer(reader.read())

可以用nested简化写法:

with contextlib.nested(open('fileToRead.txt', 'r'),
open('fileToWrite.txt', 'w')) as (reader, writer):
writer.write(reader.read())

python2.7后nested就过时了:

with open('fileToRead.txt', 'r') as reader,open('fileToWrite.txt', 'w') as writer:
writer.write(reader.read())

closing(object):创建上下文管理器,在执行过程离开with语句时自动执行object.close():

class Door(object) :
def open(self) :
print 'Door is opened'
def close(self) :
print 'Door is closed'
with contextlib.closing(Door()) as door :
door.open()

python上下文管理器及with语句的更多相关文章

  1. python上下文管理器ContextLib及with语句

    http://blog.csdn.net/pipisorry/article/details/50444736 with语句 with语句是从 Python 2.5 开始引入的一种与异常处理相关的功能 ...

  2. Python核心技术与实战——二一|巧用上下文管理器和with语句精简代码

    我们在Python中对于with的语句应该是不陌生的,特别是在文件的输入输出操作中,那在具体的使用过程中,是有什么引伸的含义呢?与之密切相关的上下文管理器(context manager)又是什么呢? ...

  3. Python上下文管理器你学会了吗?

    ​什么是上下文管理器 对于像文件操作.连接数据库等资源管理的操作,我们必须在使用完之后进行释放,不然就容易造成资源泄露.为了解决这个问题,Python的解决方式便是上下文管理器.上下文管理器能够帮助你 ...

  4. Python中的上下文管理器和with语句

    Python2.5之后引入了上下文管理器(context manager),算是Python的黑魔法之一,它用于规定某个对象的使用范围.本文是针对于该功能的思考总结. 为什么需要上下文管理器? 首先, ...

  5. Python进阶(上下文管理器与with语句)

    /*上下文管理器必须有__enter__和__exit__方法*/ class MyResource: def __enter__(self): print('链接资源') return self / ...

  6. python 上下文管理器

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 上下文管理器(context manager)是Python2.5开始支持的一种语 ...

  7. Python 上下文管理器和else块

    最终,上下文管理器可能几乎与子程序(subroutine)本身一样重要.目前,我们只了解了上下文管理器的皮毛--Basic 语言有with 语句,而且很多语言都有.但是,在各种语言中 with 语句的 ...

  8. Python上下文管理器

    在Python中让自己创建的函数.类.对象支持with语句,就实现了上线文管理协议.我们经常使用with open(file, "a+") as f:这样的语句,无需手动调用f.c ...

  9. Python上下文管理器 with

    对于系统资源的操作,如:文件操作.数据库操作等,我们往往打开文件.连接数据库后忘了将其close掉,这时就可能会引发异常,因此我们常用的做法是: # coding:utf-8 f = open(&qu ...

随机推荐

  1. 功能丰富的 Perl:轻松调试 Perl

    http://www.ibm.com/developerworks/cn/linux/sdk/perl/culture-4/index.html

  2. 文件I/O(不带缓冲)之I/O的效率

    程序清单3-3中的程序使用read和write函数复制文件.关于该程序应注意下列各点: 它从标准输入读,写至标准输出,这就假定在执行本程序之前,这些标准输入.输出已由shell安排好.确实,所有常用的 ...

  3. .net core 1.1.0 MVC 控制器接收Json字串 (JObject对象) (二)

    .net core 1.1.0 MVC 控制器接收Json字串 (JObject对象) (二) .net core 1.1.0 MVC 控制器接收Json字串 (JObject对象) (一) 上一篇主 ...

  4. 配置SQL Server 2008管理器

    SQl Server 配置管理器(简称为配置管理器)包含了SQL Server 2008服务.SQL Server 2008网络配置和SQL Native Client配置3个工具,供数据库管理人员做 ...

  5. select option jquery javascript

    jQuery获取Select选择的Text和Value: $('#myselect').find('option:selected').attr('ent_id');      $('#ent_id' ...

  6. phpcms v9 模板标签说明整理

    1.{template "content","header"} 2.网站网址调用:{siteurl($siteid)}: 3.标签get:分页,{pc:get ...

  7. 深入理解Binder(二),Binder是什么?

    上篇文章深入理解Binder(一),从AIDL谈起我们介绍了AIDL的基本使用,用AIDL两个App的通信是实现了,可是又有小伙伴疑惑了,为什么使用AIDL就能够实现两个App之间的通信?本文我们就来 ...

  8. 设置N秒后执行某个方法或函数

    设置N秒后执行一个函数,最常用的是设置一个定时器,今天刚看到有这样一个函数,感觉还是比较简单实用的,就先记下来,免得忘记了. 5秒后执行pushSecondController这个函数 [self p ...

  9. android.os.NetworkOnMainThreadException 异常处理

    当我试图在UI线程(MainActivity)连接网络的时候,运行时抛出异常droid.os.NetworkOnMainThreadException 安卓的官方文档说 The exception t ...

  10. java 过滤器Filter中chain.doFilter()之前和之后代码的执行顺序

    过滤器拦截到响应url的请求后会先执行doFilter()方法中chain.doFilter()之前的代码,然后执行下一个过滤器或者servelt.紧接着执行chain.doFilter()之后的代码 ...