详解Python中contextlib上下文管理模块的用法


Posted in Python onJune 28, 2016

咱们用的os模块,读取文件的时候,其实他是含有__enter__ __exit__ 。  一个是with触发的时候,一个是退出的时候。

with file('nima,'r') as f:
  print f.readline()

那咱们自己再实现一个标准的可以with的类。 我个人写python的时候,喜欢针对一些需要有关闭逻辑的代码,构造成with的模式 。 

#encoding:utf-8
class echo:
  def __enter__(self):
    print 'enter'
 
  def __exit__(self,*args):
    print 'exit'
 
with echo() as e:
  print 'nima'

contextlib是个比with优美的东西,也是提供上下文机制的模块,它是通过Generator装饰器实现的,不再是采用__enter__和__exit__。contextlib中的contextmanager作为装饰器来提供一种针对函数级别的上下文管理机制。

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

我这里再贴下我上次写的redis分布式锁代码中有关于contextlib的用法。其实乍一看,用了with和contextlib麻烦了,但是最少让你的主体代码更加鲜明了。

from contextlib import contextmanager
from random import random
 
DEFAULT_EXPIRES = 15
DEFAULT_RETRIES = 5
 
@contextmanager
def dist_lock(key, client):
  key = 'lock_%s' % key
 
  try:
    _acquire_lock(key, client)
    yield
  finally:
    _release_lock(key, client)
 
def _acquire_lock(key, client):
  for i in xrange(0, DEFAULT_RETRIES):
    get_stored = client.get(key)
    if get_stored:
      sleep_time = (((i+1)*random()) + 2**i) / 2.5
      print 'Sleeipng for %s' % (sleep_time)
      time.sleep(sleep_time)
    else:
      stored = client.set(key, 1)
      client.expire(key,DEFAULT_EXPIRES)
      return
  raise Exception('Could not acquire lock for %s' % key)
 
def _release_lock(key, client):
  client.delete(key)

Context Manager API

一个上下文管理器通过with声明激活, 而且API包含两个方法。__enter__()方法运行执行流进入到with代码块内。他返回一个对象共上下文使用。当执行流离开with块时,__exit__()方法上下文管理器清除任何资源被使用。

class Context(object):
  
  def __init__(self):
    print '__init__()'

  def __enter__(self):
    print '__enter__()'
    return self

  def __exit__(self, exc_type, exc_val, exc_tb):
    print '__exit__()'

with Context():
  print 'Doing work in the context.'

打印结果

__init__()
__enter__()
Doing work in the context.
__exit__()

执行上下文管理器时会调用__enter__离开时调用__exit__。

__enter__能返回任意对象,联合一个指定名字于with声明。

class WithinContext(object):

  def __init__(self, context):
    print 'WithinContext.__init__(%s)' % context

  def do_something(self):
    print 'WithinContext.do_something()'

  def __del__(self):
    print 'WithinContext.__del__'


class Context(object):

  def __init__(self):
    print '__init__()'
  
  def __enter__(self):
    print '__enter__()'
    return WithinContext(self)
  
  def __exit__(self, exc_type, exc_val, exc_tb):
    print '__exit__()'

with Context() as c:
  c.do_something()

打印结果

__init__()
__enter__()
WithinContext.__init__(<__main__.Context object at 0x7f579d8e4890>)
WithinContext.do_something()
__exit__()
WithinContext.__del__

如果上下文管理器能处理异常,__exit__()应该返回一个True值表明这个异常不需要传播,返回False异常会在执行__exit__之后被引起。

class Context(object):

  def __init__(self, handle_error):
    print '__init__(%s)' % handle_error
    self.handle_error = handle_error
  
  def __enter__(self):
    print '__enter__()'
    return self
  
  def __exit__(self, exc_type, exc_val, exc_tb):
    print '__exit__(%s, %s, %s)' % (exc_type, exc_val, exc_tb)
    return self.handle_error

with Context(True):
  raise RuntimeError('error message handled')

print

with Context(False):
  raise RuntimeError('error message propagated')

打印结果

__init__(True)
__enter__()
__exit__(<type 'exceptions.RuntimeError'>, error message handled, <traceback object at 0x7fdfb32f8b00>)

__init__(False)
__enter__()
__exit__(<type 'exceptions.RuntimeError'>, error message propagated, <traceback object at 0x7fdfb32f8b90>)
Traceback (most recent call last):
 File "test.py", line 23, in <module>
   raise RuntimeError('error message propagated')
   RuntimeError: error message propagated

从生成器到上下文管理器

创建上下文管理的传统方法,通过编写一个类与__enter__()和__exit__()方法,并不困难。但有时比你需要的开销只是管理一个微不足道的上下文。在这类情况下,您可以使用contextmanager() decorat or 生成器函数转换成一个上下文管理器。

import contextlib

@contextlib.contextmanager
def make_context():
  print ' entering'
  try:
    yield {}
   except RuntimeError, err:
    print ' Error:', err
  finally:
    print ' exiting'
    
print 'Normal:'

with make_context() as value:
  print ' inside with statement:', value
  
print
print 'handled ereor:'

with make_context() as value:
  raise RuntimeError('show example of handling an error')

print
print 'unhandled error:'

with make_context() as value:
  raise ValueError('this exception is not handled')

打印结果

Normal:
 entering
 inside with statement: {}
  exiting

handled ereor:
entering
 Error: show example of handling an error
 exiting

unhandled error:
entering
exiting
Traceback (most recent call last):
 File "test.py", line 30, in <module>
   raise ValueError('this exception is not handled')
   ValueError: this exception is not handled

嵌套上下文

使用nested()可以同时管理多个上下文。

import contextlib

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

with contextlib.nested(make_context('A'), make_context('B'), make_context('C')) as (A, B,   C):
  print 'inside with statement:', A, B, C

打印结果

entering: A
entering: B
entering: C
inside with statement: A B C
exiting: C
exiting: B
exiting: A

因为Python 2.7和以后的版本不赞成使用nested(),因为可以直接嵌套

import contextlib

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

with make_context('A') as A, make_context('B') as B, make_context('C') as C:
  print 'inside with statement:', A, B, C

关闭open的句柄

文件类支持上下文管理器, 但是有一些对象不支持。还有一些类使用close()方法但是不支持上下文管理器。我们使用closing()来为他创建一个上下文管理器。(类必须有close方法)

import contextlib


class Door(object):
  def __init__(self):
    print ' __init__()'
    
  def close(self):
    print ' close()'

print 'Normal Example:'
with contextlib.closing(Door()) as door:
  print ' inside with statement'
  
print 
print 'Error handling example:'
try:
  with contextlib.closing(Door()) as door:
    print ' raising from inside with statement'
    raise RuntimeError('error message')
except Exception, err:
  print ' Had an error:', err

打印结果

Normal Example:
  __init__()
  inside with statement
  close()

Error handling example:
  __init__()
  raising from inside with statement
  close()
  Had an error: error message
Python 相关文章推荐
Python中使用第三方库xlrd来读取Excel示例
Apr 05 Python
python类:class创建、数据方法属性及访问控制详解
Jul 25 Python
利用Python循环(包括while&amp;for)各种打印九九乘法表的实例
Nov 06 Python
Sanic框架配置操作分析
Jul 17 Python
Python + selenium + requests实现12306全自动抢票及验证码破解加自动点击功能
Nov 23 Python
对python多线程中Lock()与RLock()锁详解
Jan 11 Python
python  文件的基本操作 菜中菜功能的实例代码
Jul 17 Python
python正则-re的用法详解
Jul 28 Python
MxNet预训练模型到Pytorch模型的转换方式
May 25 Python
浅谈Pycharm的项目文件名是红色的原因及解决方式
Jun 01 Python
python实现数学模型(插值、拟合和微分方程)
Nov 13 Python
使用python实现学生信息管理系统
Feb 25 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
Python中的字符串替换操作示例
Jun 27 #Python
Python的string模块中的Template类字符串模板用法
Jun 27 #Python
You might like
PHP中的日期处理方法集锦
2007/01/02 PHP
dedecms中显示数字验证码的修改方法
2007/03/21 PHP
PHP 伪静态隐藏传递参数名的四种方法
2010/02/22 PHP
php 常用类汇总 推荐收藏
2010/05/13 PHP
关于Zend Studio 配色方案插件的介绍
2013/06/24 PHP
PHP调用VC编写的COM组件实例
2014/03/29 PHP
PHP 下载文件时如何自动添加bom头及解释BOM头和去掉bom头的方法
2016/01/04 PHP
PHP记录和读取JSON格式日志文件
2016/07/07 PHP
php中try catch捕获异常实例详解
2020/08/06 PHP
JavaScript去掉数组中的重复元素
2011/01/13 Javascript
JavaScript代码简单实现求杨辉三角给定行的最大值
2013/10/29 Javascript
jQuery中:header选择器用法实例
2014/12/29 Javascript
理解Javascript的call、apply
2015/12/16 Javascript
动态创建按钮的JavaScript代码
2016/01/29 Javascript
JS数组排序方法实例分析
2016/12/16 Javascript
浅谈js中startsWith 函数不能在任何浏览器兼容的问题
2017/03/01 Javascript
Node.js实现发送邮件功能
2017/11/06 Javascript
Vue修改mint-ui默认样式的方法
2018/02/03 Javascript
在vue中使用cookie记住用户上次选择的实例(本次例子中为下拉框)
2020/09/11 Javascript
[05:43]VG.R战队教练Mikasa专访:为目标从未停止战斗
2016/08/02 DOTA
Python内置函数—vars的具体使用方法
2017/12/04 Python
python实现redis三种cas事务操作
2017/12/19 Python
python使用mysql的两种使用方式
2018/03/07 Python
Python开发最牛逼的IDE——pycharm
2018/08/01 Python
Python基于OpenCV实现人脸检测并保存
2019/07/23 Python
django ManyToManyField多对多关系的实例详解
2019/08/09 Python
Python使用selenium + headless chrome获取网页内容的方法示例
2019/10/16 Python
tensorflow入门:TFRecordDataset变长数据的batch读取详解
2020/01/20 Python
python matplotlib工具栏源码探析三之添加、删除自定义工具项的案例详解
2021/02/25 Python
实例讲解使用HTML5 Canvas绘制阴影效果的方法
2016/03/25 HTML / CSS
医科大学生毕业的自我评价分享
2013/11/12 职场文书
重阳节活动总结
2014/08/27 职场文书
幼儿园元旦主持词
2015/07/06 职场文书
Java 超详细讲解设计模式之中的抽象工厂模式
2022/03/25 Java/Android
java中为什么说子类的构造方法默认访问的是父类的无参构造方法
2022/04/13 Java/Android
浅谈音视频 pts dts基本概念及理解
2022/08/05 数码科技