详解Python中contextlib上下文管理模块的用法
admin
2023-08-02 04:49:43
0

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

相关内容

热门资讯

Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
Apache Doris 2.... 亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...
python清除字符串里非数字... 本文实例讲述了python清除字符串里非数字字符的方法。分享给大家供大家参考。具体如下: impor...