网络编程
位置:首页>> 网络编程>> Python编程>> 详解Python中contextlib上下文管理模块的用法

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

作者:cangmean  发布时间:2022-03-10 22:32:51 

标签:Python,contextlib

咱们用的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
0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com