详解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实现从订阅源下载图片的方法
Mar 11 Python
详解Python中find()方法的使用
May 18 Python
以视频爬取实例讲解Python爬虫神器Beautiful Soup用法
Jan 20 Python
python模块之time模块(实例讲解)
Sep 13 Python
Scrapy抓取京东商品、豆瓣电影及代码分享
Nov 23 Python
python ansible服务及剧本编写
Dec 29 Python
python3.6.3+opencv3.3.0实现动态人脸捕获
May 25 Python
浅谈Django+Gunicorn+Nginx部署之路
Sep 11 Python
python实现批量文件重命名
Oct 31 Python
python GUI库图形界面开发之PyQt5日期时间控件QDateTimeEdit详细使用方法与实例
Feb 27 Python
OpenCV利用python来实现图像的直方图均衡化
Oct 21 Python
python 6种方法实现单例模式
Dec 15 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
Search File Contents PHP 搜索目录文本内容的代码
2010/02/21 PHP
PHP imagecreatefrombmp 从BMP文件或URL新建一图像
2012/07/16 PHP
PHP函数import_request_variables()用法分析
2016/04/02 PHP
PHP批量删除jQuery操作
2017/07/23 PHP
php微信开发之关键词回复功能
2018/06/13 PHP
让textarea控件的滚动条怎是位与最下方
2007/04/20 Javascript
Javascript 事件流和事件绑定
2009/07/16 Javascript
jquery下json数组的操作实现代码
2010/08/09 Javascript
js+css实现的简单易用兼容好的分页
2013/12/30 Javascript
javascript 模拟坦克大战游戏(html5版)附源码下载
2014/04/08 Javascript
jQuery中data()方法用法实例
2014/12/27 Javascript
JavaScript Window浏览器对象模型方法与属性汇总
2015/04/20 Javascript
实现隔行换色效果的两种方式【实用】
2016/11/27 Javascript
JS实现的驼峰式和连字符式转换功能分析
2016/12/21 Javascript
简单的vue-resourse获取json并应用到模板示例
2017/02/10 Javascript
Vue.js学习笔记之修饰符详解
2017/07/25 Javascript
JS实现的将html转为pdf功能【基于浏览器端插件jsPDF】
2018/02/06 Javascript
Vue引入sass并配置全局变量的方法
2018/06/27 Javascript
vue实现页面滚动到底部刷新
2019/08/16 Javascript
[04:54]DOTA2 2017国际邀请赛:上届冠军WINGS采访短片
2017/08/09 DOTA
线程和进程的区别及Python代码实例
2015/02/04 Python
Python实现读取邮箱中的邮件功能示例【含文本及附件】
2017/08/05 Python
Python抓取框架Scrapy爬虫入门:页面提取
2017/12/01 Python
Go/Python/Erlang编程语言对比分析及示例代码
2018/04/23 Python
Python猴子补丁知识点总结
2020/01/05 Python
ColourPop美国官网:卡拉泡泡,洛杉矶彩妆品牌
2019/04/28 全球购物
日语专业个人的求职信
2013/12/03 职场文书
2014年计生标语
2014/06/23 职场文书
教师一帮一活动总结
2014/07/08 职场文书
结婚通知短信怎么写
2015/04/17 职场文书
2015年医院后勤工作总结
2015/05/20 职场文书
公司借款担保书
2015/09/22 职场文书
2016年“5.12”护士节慰问信
2015/11/30 职场文书
2016关于学习党章的心得体会
2016/01/15 职场文书
ORACLE查看当前账号的相关信息
2021/06/18 Oracle
Mysql数据库group by原理详解
2022/07/07 MySQL