函数在 Python 中同时充当装饰器和上下文管理器?

这可能有点过火了,但主要是出于好奇。

有没有可能有一个可调用的对象(函数/类)同时作为 都有一个上下文管理器和一个装饰器:

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')
29975 次浏览

从 Python 3.2开始,标准库中甚至包含了对此的支持。从类 contextlib.ContextDecorator派生使得编写既可以用作装饰器又可以用作上下文管理器的类变得很容易。这个功能可以很容易地被移植到 Python 2.x 上——下面是一个基本的实现:

class ContextDecorator(object):
def __call__(self, f):
@functools.wraps(f)
def decorated(*args, **kwds):
with self:
return f(*args, **kwds)
return decorated

从这个类派生上下文管理器,并像往常一样定义 __enter__()__exit__()方法。

这里有一个例子:

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()

这张照片:

init 1 2
enter
with
exit
init 3 4
call
about to call
sample
done calling
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

现在你可以做:

mydeco = Decontext(some_context_manager)

两者都可以

@mydeco
def foo(...):
do_bar()


foo(...)

还有

with mydeco:
do_bar()

在 Python 3.2 + 中,您可以使用 @contextlib.contextmanager定义一个上下文管理器,它也是一个装饰器。

来自文件:

contextmanager() 使用 ContextDecorator,因此它创建的上下文管理器既可以用作装饰器,也可以用于 with语句

示例用法:

> > > < b > < i > from contextlib import contextmanager
> > > < i >@contextmanager 
... < i > def example _ manager (message) : 
... 打印(“开始”,消息)
... 尝试
... 屈服
... 最后:
... 打印(“完成”,消息)
...
> > 使用 example _ manager (“打印 Hello World”) < i > :
... 打印(‘ Hello,World!’) 
...
开始打印 Hello World
你好,世界!
打印完 Hello World 了
> >
> > < i >@example _ manager (‘ running my function’)
... < i > def some _ function () : 
... < i > print (‘ Inside my function’) 
...
> > > Some _ function ()
开始运行我的函数
在我的职能范围内
我的任务完成了

虽然我同意(并且支持)@jterrace,但是我在这里添加了一个非常小的变体,它返回修饰函数,并且包含了修饰函数和修饰函数的参数。

class Decon:
def __init__(self, a=None, b=None, c=True):
self.a = a
self.b = b
self.c = c


def __enter__(self):
# only need to return self
# if you want access to it
# inside the context
return self


def __exit__(self, exit_type, exit_value, exit_traceback):
# clean up anything you need to
# otherwise, nothing much more here
pass


def __call__(self, func):
def decorator(*args, **kwargs):
with self:
return func(*args, **kwargs)
return decorator