如何在不使用python 2.7中的类的情况下使上下文管理器也成为装饰器?
我用下面的风格编写了一些现有的代码,它工作正常,我可以使用chunkUndo作为上下文管理器如何在不使用python 2.7中的类的情况下使上下文管理器也成为装饰器?,python,decorator,contextmanager,Python,Decorator,Contextmanager,我用下面的风格编写了一些现有的代码,它工作正常,我可以使用chunkUndo作为上下文管理器 from contextlib import contextmanager from functools import wraps @contextmanager def chunkUndo(mode='simple'): print 'mode:',mode print 'undo chunk start' try: yield finally:
from contextlib import contextmanager
from functools import wraps
@contextmanager
def chunkUndo(mode='simple'):
print 'mode:',mode
print 'undo chunk start'
try:
yield
finally:
print 'undo chunk end'
# this works
with chunkUndo():
print 'do work here'
然而,由于我已经编写了很多类似上面的上下文管理器代码,所以我不想将它们更改为。相反,我想向已装饰的chunkUndo添加一个装饰器,使其成为装饰器函数,类似于:
def makeContextDecorator(f):
@wraps(f)
def wrapper(*args, **kw):
# some code here
return wrapper
@makeContextDecorator
@contextmanager
def chunkUndo(mode='simple'):
print 'mode:',mode
print 'undo chunk start'
try:
yield
finally:
print 'undo chunk end'
@chunkUndo
def do_work():
print 'do work'
do_work()
最终的结果是我可以使用
chunkUndo
作为装饰器和上下文管理器,也可以将参数传递给它。如何做到这一点?如果您希望chunkUndo
既可以作为上下文管理器,又可以作为应用于它所修饰的函数的装饰器,那么您需要使makeContextDecorator
函数更加复杂。以下是两个示例中都适用的第一次尝试:
from functools import wraps
def makeContextDecorator(cm):
def wrapper(func=None):
if func is not None:
@wraps(func)
def inner(*args, **kwargs):
with cm():
return func(*args, **kwargs)
return inner
else:
return cm()
return wrapper
下面是它在实际中的表现:
@makeContextDecorator
@contextlib.contextmanager
def foo():
print("start")
try:
yield
finally:
print("end")
with foo():
print("with") # prints "start", "with", "end" on separate lines
@foo
def bar(x):
print("bar", x)
bar(1) # prints "start", "bar 1", "end" on separate lines
此设计仅适用于不接受任何参数的上下文管理器
您可以在中使用带有参数的语句(您只需要更改包装器以接受*args
和**kwargs
样式的参数),但这会有点尴尬,因为您无法区分使用单个可调用参数(例如和foo(lambda x:x*2)调用之间的区别:
)并作为装饰程序调用
当您使用decorator语法时,接受上下文管理器的参数将更加困难。这是因为使用参数调用decorator(例如@foo(“xyz”)
)意味着decorator实际上是一个decorator工厂。它需要返回类似于decorator(修改另一个函数的函数)的内容。但是返回的值也需要像上下文管理器一样直接工作。您需要编写一个可以同时完成这两个任务的类,这就是您所说的要避免的。我是否正确理解您希望chunkUndo
在将其更改为在下面的代码中工作后,仍能像在顶部的代码中一样工作(在with
语句中)?我怀疑您的makeContextDecorator
函数在这种情况下需要更加复杂(可能是一个类,而不仅仅是一个函数)。我们的目标是使chunkUndo
既可以作为上下文管理器
又可以作为装饰器
,但不使用类。为什么需要在不使用类的情况下这样做?我的意思是,我和下一个家伙一样喜欢用精神状态敲打指甲,但在某种程度上,你只需要按照语言所要求的方式去做。听起来真的,你要找的不是“不上课”。这是“没有为每个装饰经理编写新类”。如果你根本不想上课,那你就倒霉了。