Python 上下文管理器到装饰器(反之)
Python context manager to decorator (and inversely)
我想要:
# 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
块,然后通过装饰器和[=39=重新使用它] 上下文管理器 同样:通过编写 try/except 块 仅一次 ,无论是在生成器函数还是任何其他包装器中。
ContextDecorator
东西(在 contextlib
in py3 / contextlib2
in py2)只能用于 classes,但在那种情况下似乎没用...我错过了什么吗?有没有办法使用 __enter__
和 __exit__
使用基于 class 的 ContextManager 来实现我的 try/except 块?
或者是否有可能将使用 yield
语法构建的上下文管理器转换为装饰器?
或者相反(上下文管理器的装饰器)?
如果不是,很高兴知道 Python 的限制是什么。
据我了解,yield
语法与 Python 解释器和上下文切换紧密相关,我不知道是否可以更改其在这一点上的行为。
通过组合 contextmanager
用于管理其上下文 (_GeneratorContextManager
) 的 class 和 ContextDecorator
class,您可以轻松实现所需的功能.例如
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
比 Dunes 的解决方案更容易理解,尽管没有利用 ContextDecorator
双语法。
import contextlib
import functools
def handler():
try:
yield
except:
print 'bar'
my_contextmanager = contextlib.contextmanager(handler)
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
with my_contextmanager():
func(*args, **kwargs)
return wrapper
with my_contextmanager():
raise Exception('baz')
@my_decorator
def f():
raise Exception('baz')
f()
给出:
bar
bar
我想要:
# 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
块,然后通过装饰器和[=39=重新使用它] 上下文管理器 同样:通过编写 try/except 块 仅一次 ,无论是在生成器函数还是任何其他包装器中。
ContextDecorator
东西(在 contextlib
in py3 / contextlib2
in py2)只能用于 classes,但在那种情况下似乎没用...我错过了什么吗?有没有办法使用 __enter__
和 __exit__
使用基于 class 的 ContextManager 来实现我的 try/except 块?
或者是否有可能将使用 yield
语法构建的上下文管理器转换为装饰器?
或者相反(上下文管理器的装饰器)?
如果不是,很高兴知道 Python 的限制是什么。
据我了解,yield
语法与 Python 解释器和上下文切换紧密相关,我不知道是否可以更改其在这一点上的行为。
通过组合 contextmanager
用于管理其上下文 (_GeneratorContextManager
) 的 class 和 ContextDecorator
class,您可以轻松实现所需的功能.例如
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
比 Dunes 的解决方案更容易理解,尽管没有利用 ContextDecorator
双语法。
import contextlib
import functools
def handler():
try:
yield
except:
print 'bar'
my_contextmanager = contextlib.contextmanager(handler)
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
with my_contextmanager():
func(*args, **kwargs)
return wrapper
with my_contextmanager():
raise Exception('baz')
@my_decorator
def f():
raise Exception('baz')
f()
给出:
bar
bar