我想拥有:

# Simple example, one could replace try/except by any other nested construct
def mycontextmanager_generator(foo):
    try:
        yield
    except:
        print 'bar'
        raise

mycontextmanager = build_contextmanager(mycontextmanager_generator)
mydecorator = build_decorator(mycontextmanager_generator)


>>> with mycontextmanager():
>>>     raise Exception('baz gone bar in context manager')
... bar


>>> @mydecorator()
>>> def bazzer():
>>>     raise Exception('baz gone bar in decorator')
>>> bazzer()
... bar


在此示例中,我从生成器函数构建上下文管理器,并从同一函数构建装饰器。这是我尝试以不成功的方式实现的目标。

更一般而言,我要的是DRY:编写一次try/except块,然后通过装饰器和上下文管理器再次使用它:再次:通过仅一次编写try / except bloc,无论是在生成器函数中还是在生成器函数中任何其他包装。

ContextDecorator事物(在py3中的contextlib中/在py2中的contextlib2中)仅可用于类,但在这种情况下似乎没有用……我错过了什么吗?是否可以使用__enter____exit__使用基于类的ContextManager来实现try / except块?

还是有可能将使用yield语法构建的contextmanager转换为装饰器?

还是相反(contextmanager的装饰器)?

如果没有,很高兴知道Python在此方面的局限性。

据我了解,yield语法与Python解释器和上下文切换紧密绑定,我不知道是否有可能在那一点上改变其行为。

最佳答案

通过组合contextmanager用于管理其上下文的类(_GeneratorContextManager)和ContextDecorator类,可以轻松实现所需的功能。例如。

from contextlib import ContextDecorator, _GeneratorContextManager
from functools import wraps

class MyContextManager(_GeneratorContextManager, ContextDecorator):
    pass

def contextmanager(func):
    @wraps(func)
    def helper(*args, **kwds):
        return MyContextManager(func, args, kwds)
    return helper

@contextmanager
def print_bar_on_error():
    try:
        yield
    except:
        print('bar')
        raise

with print_bar_on_error():
    raise Exception('baz gone bar in context manager')


产生:

bar
Traceback (most recent call last):
  File "run.py", line 28, in <module>
    raise Exception('baz gone bar in context manager')
Exception: baz gone bar in context manager


当用作装饰

@print_bar_on_error()
def bazzer():
    raise Exception('baz gone bar in decorator')
bazzer()


产生:

bar
Traceback (most recent call last):
  File "run.py", line 32, in <module>
    bazzer()
  File "c:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\contextlib.py", line 30, in inner
    return func(*args, **kwds)
  File "run.py", line 31, in bazzer
    raise Exception('baz gone bar in decorator')
Exception: baz gone bar in decorator
    return func(*args, **kwds)
Exception: baz gone bar in decorator

关于python - Python上下文管理器到装饰器(反之),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38264864/

10-16 06:19