详解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中使用PDB库调试程序
Apr 05 Python
python字典get()方法用法分析
Apr 17 Python
Python实现提取文章摘要的方法
Apr 21 Python
Python虚拟环境Virtualenv使用教程
May 18 Python
Python的网络编程库Gevent的安装及使用技巧
Jun 24 Python
python实现多线程行情抓取工具的方法
Feb 28 Python
python读取LMDB中图像的方法
Jul 02 Python
Python Pillow Image Invert
Jan 22 Python
Python入门Anaconda和Pycharm的安装和配置详解
Jul 16 Python
PyCharm2018 安装及破解方法实现步骤
Sep 09 Python
解决Pytorch训练过程中loss不下降的问题
Jan 02 Python
Python tkinter界面实现历史天气查询的示例代码
Aug 23 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
Ha0k 0.3 PHP 网页木马修改版
2009/10/11 PHP
检测png图片是否完整的php代码
2010/09/06 PHP
PHP使用array_multisort对多个数组或多维数组进行排序
2014/12/16 PHP
PHP中使用Memache作为进程锁的操作类分享
2015/03/30 PHP
详解js闭包
2014/09/02 Javascript
js弹出对话框方式小结
2015/11/17 Javascript
jQuery对象的链式操作用法分析
2016/05/10 Javascript
javascript日期比较方法实例分析
2016/06/17 Javascript
利用JQuery直接调用asp.net后台的简单方法
2016/10/27 Javascript
概述一个页面从输入URL到页面加载完的过程
2016/12/16 Javascript
JS实现的RGB网页颜色在线取色器完整实例
2016/12/21 Javascript
vue.js中过滤器的使用教程
2017/06/08 Javascript
基于angular2 的 http服务封装的实例代码
2017/06/29 Javascript
Angular2+如何去除url中的#号详解
2017/12/20 Javascript
浅谈ajax请求不同页面的微信JSSDK问题
2018/02/26 Javascript
微信小程序云开发如何实现数据库自动备份实现
2019/08/16 Javascript
JavaScript 接口原理与用法实例详解
2020/05/12 Javascript
Python函数中定义参数的四种方式
2014/11/30 Python
python中lambda与def用法对比实例分析
2015/04/30 Python
Python中使用Beautiful Soup库的超详细教程
2015/04/30 Python
python开发简易版在线音乐播放器
2017/03/03 Python
Python 专题六 局部变量、全局变量global、导入模块变量
2017/03/20 Python
python命令行解析之parse_known_args()函数和parse_args()使用区别介绍
2018/01/24 Python
pandas的排序和排名的具体使用
2019/07/31 Python
Django使用 Bootstrap 样式修改书籍列表过程解析
2019/08/09 Python
pandas按行按列遍历Dataframe的几种方式
2019/10/23 Python
Python openpyxl 插入折线图实例
2020/04/17 Python
python 判断一组数据是否符合正态分布
2020/09/23 Python
CSS3 选择器 伪类选择器介绍
2012/01/21 HTML / CSS
HTML5之SVG 2D入门6—视窗坐标系与用户坐标系及变换概述
2013/01/30 HTML / CSS
怎样有效的进行自我评价
2013/10/06 职场文书
商务英语专业应届毕业生求职信
2013/10/28 职场文书
平面网站制作专科生的自我评价分享
2013/12/11 职场文书
镇班子对照检查材料思想汇报
2014/09/24 职场文书
车辆年检委托书范本
2014/10/14 职场文书
HTML+CSS制作心跳特效的实现
2021/05/26 HTML / CSS