Python中的with语句与上下文管理器学习总结


Posted in Python onJune 28, 2016

0、关于上下文管理器
上下文管理器是可以在with语句中使用,拥有__enter__和__exit__方法的对象。

with manager as var:
  do_something(var)

相当于以下情况的简化:

var = manager.__enter__()
try:
  do_something(var)
finally:
  manager.__exit__()

换言之,PEP 343中定义的上下文管理器协议允许将无聊的try...except...finally结构抽象到一个单独的类中,仅仅留下关注的do_something部分。

__enter__方法首先被调用。它可以返回赋给var的值。as部分是可选的:如果它不出现,enter的返回值简单地被忽略。
with语句下的代码被执行。就像try子句,它们或者成功执行到底,或者break,continue或return,或者可以抛出异常。无论哪种情况,该块结束后,__exit__方法被调用。如果抛出异常,异常信息被传递给__exit__,这将在下一章节讨论。通常情况下,异常可被忽略,就像在finally子句中一样,并且将在__exit__结束后重新抛出。
比如说我们想确认一个文件在完成写操作之后被立即关闭:

>>> class closing(object):
...  def __init__(self, obj):
...   self.obj = obj
...  def __enter__(self):
...   return self.obj
...  def __exit__(self, *args):
...   self.obj.close()
>>> with closing(open('/tmp/file', 'w')) as f:
...  f.write('the contents\n')

这里我们确保了当with块退出时调用了f.close()。因为关闭文件是非常常见的操作,该支持已经出现在file类之中。它有一个__exit__方法调用close,并且本身可作为上下文管理器。

>>> with open('/tmp/file', 'a') as f:
...  f.write('more contents\n')

try...finally常见的用法是释放资源。各种不同的情况实现相似:在__enter__阶段资源被获得,在__exit__阶段释放,如果抛出异常也被传递。正如文件操作,往往这是对象使用后的自然操作,内置支持使之很方便。每一个版本,Python都在更多的地方提供支持。

1、如何使用上下文管理器:

如何打开一个文件,并写入"hello world"

filename="my.txt"
mode="w"
writer=open(filename,mode)
writer.write("hello world")
writer.close()

当发生异常时(如磁盘写满),就没有机会执行第5行。当然,我们可以采用try-finally语句块进行包装:

writer=open(filename,mode)
try:
  writer.write("hello world")
finally:
  writer.close()

当我们进行复杂的操作时,try-finally语句就会变得丑陋,采用with语句重写:

with open(filename,mode) as writer:
  writer.write("hello world")

as指代了从open()函数返回的内容,并把它赋给了新值。with完成了try-finally的任务。

2、自定义上下文管理器

with语句的作用类似于try-finally,提供一种上下文机制。要应用with语句的类,其内部必须提供两个内置函数__enter__和__exit__。前者在主体代码执行前执行,后者在主体代码执行后执行。as后面的变量,是在__enter__函数中返回的。

class echo():
  def output(self):
    print "hello world"
  def __enter__(self):
    print "enter"
    return self #可以返回任何希望返回的东西
  def __exit__(self,exception_type,value,trackback):
    print "exit"
    if exception_type==ValueError:
      return True
    else:
      return Flase
 
>>>with echo as e:
  e.output()

输出:
enter
hello world
exit
完备的__exit__函数如下:
def __exit__(self,exc_type,exc_value,exc_tb)

其中,exc_type:异常类型;exc_value:异常值;exc_tb:异常追踪信息

当__exit__返回True时,异常不传播

3、contextlib模块

contextlib模块的作用是提供更易用的上下文管理器,它是通过Generator实现的。contextlib中的contextmanager作为装饰器来提供一种针对函数级别的上下文管理机制,常用框架如下:

from contextlib import contextmanager
@contextmanager
def make_context():
  print 'enter'
  try:
    yield "ok"
  except RuntimeError,err:
    print 'error',err
  finally:
    print 'exit'
    
>>>with make_context() as value:
  print value

   
输出为:

enter
  ok
  exit

其中,yield写入try-finally中是为了保证异常安全(能处理异常)as后的变量的值是由yield返回。yield前面的语句可看作代码块执行前操作,yield之后的操作可以看作在__exit__函数中的操作。

以线程锁为例:

@contextlib.contextmanager
def loudLock():
  print 'Locking'
  lock.acquire()
  yield
  print 'Releasing'
  lock.release()
 
with loudLock():
  print 'Lock is locked: %s' % lock.locked()
  print 'Doing something that needs locking'
 
#Output:
#Locking
#Lock is locked: True
#Doing something that needs locking
#Releasing

4、contextlib.nested:减少嵌套

对于:

with open(filename,mode) as reader:
  with open(filename1,mode1) as writer:
    writer.write(reader.read())

可以通过contextlib.nested进行简化:

with contextlib.nested(open(filename,mode),open(filename1,mode1)) as (reader,writer):
  writer.write(reader.read())

在python 2.7及以后,被一种新的语法取代:

with open(filename,mode) as reader,open(filename1,mode1) as writer:
  writer.write(reader.read())

5、contextlib.closing()

file类直接支持上下文管理器API,但有些表示打开句柄的对象并不支持,如urllib.urlopen()返回的对象。还有些遗留类,使用close()方法而不支持上下文管理器API。为了确保关闭句柄,需要使用closing()为它创建一个上下文管理器(调用类的close方法)。

import contextlib
class myclass():
  def __init__(self):
    print '__init__'
  def close(self):
    print 'close()'
   
with contextlib.closing(myclass()):
  print 'ok'

   
输出:

__init__
ok
close()
Python 相关文章推荐
Python中__init__和__new__的区别详解
Jul 09 Python
跟老齐学Python之模块的加载
Oct 24 Python
Python实现删除当前目录下除当前脚本以外的文件和文件夹实例
Jul 27 Python
Python的Flask框架标配模板引擎Jinja2的使用教程
Jul 12 Python
深入学习Python中的上下文管理器与else块
Aug 27 Python
python timestamp和datetime之间转换详解
Dec 11 Python
python模拟预测一下新型冠状病毒肺炎的数据
Feb 01 Python
Python如何实现在字符串里嵌入双引号或者单引号
Mar 02 Python
详解python 条件语句和while循环的实例代码
Dec 28 Python
python 使用OpenCV进行简单的人像分割与合成
Feb 02 Python
浅谈Python 中的复数问题
May 19 Python
python数字图像处理之对比度与亮度调整示例
Jun 28 Python
深入解析Python中的上下文管理器
Jun 28 #Python
详解Python中contextlib上下文管理模块的用法
Jun 28 #Python
实例讲解Python中SocketServer模块处理网络请求的用法
Jun 28 #Python
Python中asyncore异步模块的用法及实现httpclient的实例
Jun 28 #Python
python 字典(dict)按键和值排序
Jun 28 #Python
简单谈谈python的反射机制
Jun 28 #Python
Python实现带百分比的进度条
Jun 28 #Python
You might like
PHP 7.1中AES加解密方法mcrypt_module_open()的替换方案
2017/10/17 PHP
Mac系统下搭建Nginx+php-fpm实例讲解
2020/12/15 PHP
javascript常用方法、属性集合及NodeList 和 HTMLCollection 的浏览器差异
2010/12/25 Javascript
利用JQuery和JS实现奇偶行背景颜色自定义效果
2012/11/19 Javascript
jquery 多行文本框(textarea)高度变化
2013/07/03 Javascript
函数式 JavaScript(一)简介
2014/07/07 Javascript
Javascript访问器属性实例分析
2014/12/30 Javascript
JavaScript实现仿淘宝商品购买数量的增减效果
2016/01/22 Javascript
使用jQuery5分钟快速搞定双色表格的简单实例
2016/08/08 Javascript
基于jQuery的AJAX和JSON实现纯html数据模板
2016/08/09 Javascript
详解ES6中的let命令
2020/04/05 Javascript
VueJS 集成 Medium Editor的示例代码 (自定义编辑器按钮)
2017/08/24 Javascript
实例讲解Vue.js中router传参
2018/04/22 Javascript
微信小程序WebSocket实现聊天对话功能
2018/07/06 Javascript
JavaScript数组排序功能简单实现
2020/05/14 Javascript
[01:46]TI4西雅图DOTA2前线报道 中国选手抱团调时差
2014/07/08 DOTA
python 打印出所有的对象/模块的属性(实例代码)
2016/09/11 Python
Python正则表达式实现截取成对括号的方法
2017/01/06 Python
Python 单元测试(unittest)的使用小结
2018/11/14 Python
实例讲解Python中浮点型的基本内容
2019/02/11 Python
Python中psutil的介绍与用法
2019/05/02 Python
Django 创建/删除用户的示例代码
2019/07/24 Python
pytorch查看torch.Tensor和model是否在CUDA上的实例
2020/01/03 Python
python 轮询执行某函数的2种方式
2020/05/03 Python
Python CategoricalDtype自定义排序实现原理解析
2020/09/11 Python
如何利用python发送邮件
2020/09/26 Python
使用OpenCV实现人脸图像卡通化的示例代码
2021/01/15 Python
H&M美国官网:欧洲最大的服饰零售商
2016/09/07 全球购物
武汉东之林科技有限公司机试
2013/09/17 面试题
自学考试自我鉴定范文
2013/09/26 职场文书
基层党建工作宣传标语
2014/06/24 职场文书
幼儿园教师自我评价
2015/03/04 职场文书
学术研讨会主持词
2015/07/04 职场文书
二十年同学聚会致辞
2015/07/28 职场文书
MySQL中连接查询和子查询的问题
2021/09/04 MySQL
Redis如何使用乐观锁(CAS)保证数据一致性
2022/03/25 Redis