详解Python中contextlib上下文管理模块的用法
作者:cangmean 发布时间:2022-03-10 22:32:51
咱们用的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')
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 'handled ereor:'
with make_context() as value:
raise RuntimeError('show example of handling an error')
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 '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
猜你喜欢
- 引言在前面的文章当中我们讨论的是 python3 当中早期的内嵌数据结构字典的实现,在本篇文章当中主要介绍在后续对于字典的内存优化。字典优化
- dom元素内部内容是动态的,重置数据后直接获取宽高总是不准确:this.$refs.editor[0].offsetHeight;原因:重置
- 本文实例讲述了Python基于回溯法子集树模板实现图的遍历功能。分享给大家供大家参考,具体如下:问题一个图:A --> BA --&g
- golang字符串比较的三种常见方法fmt.Println("go"=="go")fmt.Print
- 在任何语言中,都会规定某些对象(属性、方法、函数、类等)只能够在某个范围内访问,出了这个范围就不能访问了。这是“公”、“私”之分。此外,还会
- 情景一: 表中数据 name score aaa 11 aaa 19 bbb 12 bbb 18 ccc 19 ddd 21 期望查询结果如
- 场景需求:需要在Flask服务器的本地找一张图片返回给前端展示出来。问题疑点:通常前端的<img>标签只会接受url的形式来展示
- 这篇文章主要介绍了wxpython自定义下拉列表框过程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要
- 关于本地缓存1.wx.setStorage(wx.setStorageSync)、wx.getStorage(wx.getStorageSy
- 前言本方案只适应于小的项目、项目未上线或者紧急情况下可采用这种方式,一旦开启慢日志查询会增加数据库的压力,所以一般采用后台对数据操作时间写入
- Django项目中为什么会加载静态时会失败呢?原因:django部署方式比较特别,采用静态文件路径:STATICFILES_DIRS的部署方
- 前言文件上传漏洞大多出现在可以进行文件上传的地方,如用户头像上传,文档上传处等。该漏洞是一个危害十分大的漏洞,通过文件上传,攻击者可以上传w
- Step1:确定操作系统Python 解释器的下载地址为:https://www.python.org/ ,点击&nbs
- 在实际工作或面试中,我们经常会遇到“数组去重”问题,接下来就是使用js实现的数组去重的多种方法:1.将数组的每一个元素依次与其他元素做比较,
- 1.前言选项(Options)模式是对配置(Configuration)的功能的延伸。在12章(ASP.NET Core中的配置二)Conf
- 我们可用ADO STREAM来做一个无组件的上传程序。Stream对象包含了许多操作二进制和文本文件的方法,我们现在用Stream对象来操作
- 实验室老师让给数据画一张线性拟合图。不会matlab,就琢磨着用python。参照了网上的一些文章,查看了帮助文档,成功的写了出来这里用到了
- <?php //设置我们将要使用的文件 $srcurl = "http://localhost/index.php"
- 如下所示:"""提取文档数超过10000的数据按照某个字段的值具有唯一性进行升序,按照@timestamp进行
- Golang 开发者遇到的许多问题之一是尝试将一个函数的参数设置为可选. 这是一个非常常见的用例, 有些对象应该使用一些基本的默认设置来开箱