如何在不使用python 2.7中的类的情况下使上下文管理器也成为装饰器?

如何在不使用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:

我用下面的风格编写了一些现有的代码,它工作正常,我可以使用chunkUndo作为上下文管理器

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
既可以作为
上下文管理器
又可以作为
装饰器
,但不使用类。为什么需要在不使用类的情况下这样做?我的意思是,我和下一个家伙一样喜欢用精神状态敲打指甲,但在某种程度上,你只需要按照语言所要求的方式去做。听起来真的,你要找的不是“不上课”。这是“没有为每个装饰经理编写新类”。如果你根本不想上课,那你就倒霉了。