有没有办法在 ContextDecorator 中访问函数的 attributes/parameters?

Is there a way to access a function's attributes/parameters within a ContextDecorator?

我正在尝试使用 Python 的 contextlib.ContextDecorator class.

编写上下文管理器装饰器

有没有办法在上下文管理器中访问装饰函数的参数?

这是我正在做的一个例子:

from contextlib import ContextDecorator

class savePen(ContextDecorator):
    def __enter__(self):
        self.prevPen = self.dc.GetPen()     # AttributeError
        return self

    def __exit__(self, *exc):
        self.dc.SetPen(self.prevPen)
        return False

鉴于以上,这:

@savePen()
def func(dc, param1, param2):
    # do stuff, possibly changing the pen style

应该等同于:

def func(dc, param1, param2):
    prevPen = dc.GetPen()
    # do stuff, possibly changing the pen style
    dc.SetPen(prevPen)

我已经搜索了 contextlib 的文档,但没有找到任何有用的东西。

有谁知道如何从 ContextDecorator class 中访问装饰函数的属性?

编辑 1:

正如@chepner 在 中所说,ContextDecorator 是

的糖
def func(dc, param1, param2):
    with savePen():
        ...

并且它无法访问函数的参数。

但是,在这种情况下,在 with savePen() 内部运行的任何内容都可以访问函数参数 dcparam1param2。这让我觉得我应该能够使用 ContextDecorator 访问它们。

例如,这是有效的:

def func(dc, param1, param2):
    with savePen():
        print(param1)

contextlib.contextmanager 在这里似乎更合适。请注意,与其他任何事情一样,您无法从函数外部访问函数主体的局部变量(无论如何,缺乏自省技巧)。

@contextlib.contextmanager
def savePen(dc):
    prevPen = dc.GetPen()
    yield
    dc.SetPen(prevPen)

with savePen(dc):
    func(dc, param1, param2)

请注意,对于 ContextDecorator,上下文管理器是在没有参数的情况下实例化的,即

@savePen()
def func(dc, param1, param2):
    # do stuff, possibly changing the pen style

的语法糖(根据文档)
def func(dc, param1, param2):
    with savePen():
        ...

因此无法告诉 savePen 要使用哪个对象 (dc)。

我设法通过构建自己的装饰器来做到这一点,使用我之前在 Python2 中完成的东西。

我没有使用上下文管理器,而是使用了 try...finally 结构。

这是我想出的(我已经删除了所有做诸如使文档字符串正确的事情的绒毛):

class savePen(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        """This provides support for functions """
        dc = args[0]
        prevPen = dc.GetPen()
        try:
            retval =  self.func(*args, **kwargs)
        finally:
            dc.SetPen(prevPen)
        return retval

    def __get__(self, obj, objtype):
        """ And this provides support for instance methods """
        @functools.wraps(self.func)
        def wrapper(*args, **kwargs):
            dc = args[0]
            prevPen = dc.GetPen()
            try:
                retval = self.func(obj, *args, **kwargs)
            finally:
                dc.SetPen(prevPen)
            return retval
        return wrapper