python context manager
¿Función que actúa como decorador y administrador de contexto en Python? (4)
Esto podría estar empujando las cosas un poco demasiado lejos, pero sobre todo por curiosidad ..
¿Sería posible tener un objeto llamable (función / clase) que actúe como administrador de contexto y decorador al mismo tiempo?
def xxx(*args, **kw):
# or as a class
@xxx(foo, bar)
def im_decorated(a, b):
print(''do the stuff'')
with xxx(foo, bar):
print(''do the stuff'')
A partir de Python 3.2, el soporte para esto se incluye incluso en la biblioteca estándar. Derivar de la clase contextlib.ContextDecorator
facilita la escritura de clases que se pueden usar como un decorador o un administrador de contexto. Esta funcionalidad podría ser fácilmente devuelta a Python 2.x - aquí hay una implementación básica:
class ContextDecorator(object):
def __call__(self, f):
@functools.wraps(f)
def decorated(*args, **kwds):
with self:
return f(*args, **kwds)
return decorated
Derive su administrador de contexto de esta clase y defina los __enter__()
y __exit__()
como de costumbre.
Aquí hay un ejemplo:
class ContextDecorator(object):
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
print "init", foo, bar
def __call__(self, f):
print "call"
def wrapped_f():
print "about to call"
f()
print "done calling"
return wrapped_f
def __enter__(self):
print "enter"
def __exit__(self, exc_type, exc_val, exc_tb):
print "exit"
with ContextDecorator(1, 2):
print "with"
@ContextDecorator(3, 4)
def sample():
print "sample"
sample()
Esto imprime:
init 1 2
enter
with
exit
init 3 4
call
about to call
sample
done calling
En Python 3.2+, puedes definir un administrador de contexto que también es un decorador que usa @contextlib.contextmanager
.
De la documentación:
@contextlib.contextmanager utiliza
ContextDecorator
para que los gestores de contexto que crea puedan usarse como decoradores ywith
declaraciones
Ejemplo de uso:
>>> from contextlib import contextmanager
>>> @contextmanager
... def example_manager(message):
... print(''Starting'', message)
... try:
... yield
... finally:
... print(''Done'', message)
...
>>> with example_manager(''printing Hello World''):
... print(''Hello, World!'')
...
Starting printing Hello World
Hello, World!
Done printing Hello World
>>>
>>> @example_manager(''running my function'')
... def some_function():
... print(''Inside my function'')
...
>>> some_function()
Starting running my function
Inside my function
Done running my function
class Decontext(object):
"""
makes a context manager also act as decorator
"""
def __init__(self, context_manager):
self._cm = context_manager
def __enter__(self):
return self._cm.__enter__()
def __exit__(self, *args, **kwds):
return self._cm.__exit__(*args, **kwds)
def __call__(self, func):
def wrapper(*args, **kwds):
with self:
return func(*args, **kwds)
return wrapper
ahora puedes hacer
mydeco = Decontext(some_context_manager)
y eso permite tanto
@mydeco
def foo(...):
do_bar()
foo(...)
y
with mydeco:
do_bar()