Describes the usage of the contextlib context management module in Python,
The OS module we use actually contains _ enter _ exit _ when reading files __. One is triggered by with, and the other is exited.
with file('nima,'r') as f: print f.readline()
Then we can implement a standard with class. When I write python, I like to construct the with mode for some code that requires closing logic.
#encoding:utf-8class echo: def __enter__(self): print 'enter' def __exit__(self,*args): print 'exit' with echo() as e: print 'nima'
Contextlib is something more elegant than with and also a module that provides context mechanisms. It is implemented through the Generator modifier and is no longer implemented using _ enter _ and _ exit __. Contextmanager in contextlib provides a function-level context management mechanism.
from contextlib import contextmanager @contextmanagerdef make_context() : print 'enter' try : yield {} except RuntimeError, err : print 'error' , err finally : print 'exit' with make_context() as value : print value
I will post the usage of contextlib in the redis distributed lock code I wrote last time. In fact, at first glance, it is troublesome to use with and contextlib, but at least it makes your subject code clearer.
from contextlib import contextmanagerfrom random import random DEFAULT_EXPIRES = 15DEFAULT_RETRIES = 5 @contextmanagerdef 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
A context manager is activated through the with statement, and the API contains two methods. The _ enter _ () method runs the execution stream and enters the with code block. It returns the context of an object. When the execution stream leaves the with block, the context manager of the __exit _ () method clears any resources used.
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.'
Print results
__init__()__enter__()Doing work in the context.__exit__()
When the context manager is executed, _ enter _ call _ exit _ when leaving will be called __.
_ Enter _ can return any object, and join a specified name with declaration.
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()
Print results
__init__()__enter__()WithinContext.__init__(<__main__.Context object at 0x7f579d8e4890>)WithinContext.do_something()__exit__()WithinContext.__del__
If the context manager can handle exceptions, __exit _ () should return a value of True indicating that the exception does not need to be propagated. If False is returned, the exception will be caused after _ exit _ is executed.
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_errorwith Context(True): raise RuntimeError('error message handled')printwith Context(False): raise RuntimeError('error message propagated')
Print results
__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
From generator to context Manager
It is not difficult to create a traditional method of context management by writing a class and the _ enter _ () and _ exit _ () methods. But sometimes the overhead you need is only a negligible context for management. In this case, you can use the contextmanager () decorat or generator function to convert a context manager.
import contextlib@contextlib.contextmanagerdef 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 printprint 'handled ereor:'with make_context() as value: raise RuntimeError('show example of handling an error')printprint 'unhandled error:'with make_context() as value: raise ValueError('this exception is not handled')
Print results
Normal: entering inside with statement: {} exitinghandled ereor:entering Error: show example of handling an error exitingunhandled error:enteringexitingTraceback (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 Context
You can use nested () to manage multiple contexts at the same time.
import contextlib@contextlib.contextmanagerdef make_context(name): print 'entering:', name yield name print 'exiting:', namewith contextlib.nested(make_context('A'), make_context('B'), make_context('C')) as (A, B, C): print 'inside with statement:', A, B, C
Print results
entering: Aentering: Bentering: Cinside with statement: A B Cexiting: Cexiting: Bexiting: A
Because Python 2.7 and later versions do not support the use of nested (), because it can be directly nested
import contextlib@contextlib.contextmanagerdef make_context(name): print 'entering:', name yield name print 'exiting:', namewith make_context('A') as A, make_context('B') as B, make_context('C') as C: print 'inside with statement:', A, B, C
Close open Handle
The file class supports the context manager, but some objects are not supported. Some Classes use the close () method but do not support the context manager. We use closing () to create a context manager for him. (The class must have the close method)
import contextlibclass 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
Print results
Normal Example: __init__() inside with statement close()Error handling example: __init__() raising from inside with statement close() Had an error: error message