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

Jac*_*son 48 python decorator contextmanager

这可能会把事情推得太远,但主要是出于好奇.

是否有可能以具有一个可调用的对象(功能/类)充当两个上下文管理器,并在同一时间装饰器:

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')
Run Code Online (Sandbox Code Playgroud)

Sve*_*ach 49

从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
Run Code Online (Sandbox Code Playgroud)

从此类派生您的上下文管理器__enter__()__exit__()像往常一样定义和方法.

  • 如果必须使用Python2,则可以使用contextlib2:http://contextlib2.readthedocs.org/en/latest/ (6认同)
  • 这可能会有所帮助:https://coderwall.com/p/0lk6jg/python-decorators-vs-context-managers-have-your-cake-and-eat-it (2认同)

Mar*_*ery 25

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

来自文档:

contextmanager()采用ContextDecorator这样的背景下管理它创建可以作为装饰,以及在with声明

用法示例:

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

  • 如果“example_manager”产生结果,那么当用作装饰器时我们如何访问该结果? (4认同)
  • @coler-j 你不能从 https://docs.python.org/3/library/contextlib.html >请注意,使用上下文管理器作为函数装饰器时还有一个额外的限制:无法访问返回值__enter__() 的。如果需要该值,则仍然需要使用显式 with 语句。 (3认同)

nos*_*klo 12

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
Run Code Online (Sandbox Code Playgroud)

现在你可以这样做:

mydeco = Decontext(some_context_manager)
Run Code Online (Sandbox Code Playgroud)

这两者都允许

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

foo(...)
Run Code Online (Sandbox Code Playgroud)

with mydeco:
    do_bar()
Run Code Online (Sandbox Code Playgroud)

  • 如果上下文管理器接受参数,你如何将参数传递给装饰器? (2认同)

ope*_*onk 7

虽然我在这里同意(并赞成)@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
Run Code Online (Sandbox Code Playgroud)


jte*_*ace 5

这是一个例子:

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()
Run Code Online (Sandbox Code Playgroud)

打印:

init 1 2
enter
with
exit
init 3 4
call
about to call
sample
done calling
Run Code Online (Sandbox Code Playgroud)

  • 当用作装饰器时,它的作用方式与上下文管理器不同,这似乎是OP的意图。(请参阅OP的评论-“这基本上是在测试套件中设置固定装置的两种替代方法。”) (3认同)