咱们用的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__(, error message handled, ) __init__(False) __enter__() __exit__( , error message propagated, ) Traceback (most recent call last): File \"test.py\", line 23, in 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
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