有没有办法在 ContextDecorator 中访问函数的属性/参数?

Posted

技术标签:

【中文标题】有没有办法在 ContextDecorator 中访问函数的属性/参数?【英文标题】:Is there a way to access a function's attributes/parameters within a ContextDecorator? 【发布时间】:2015-10-22 06:36:48 【问题描述】:

我正在尝试使用 Python 的 contextlib.ContextDecorator 类编写上下文管理器装饰器。

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

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

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 类中访问修饰函数的属性?

编辑1:

正如@chepner 在this response 中所说,ContextDecorator 是糖

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

并且它不能访问函数的参数。

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

例如,这是有效的:

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

【问题讨论】:

【参考方案1】:

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)。

【讨论】:

这就是我害怕的。我想定义两个函数/类没什么大不了的,但如果只有一个就好了。我会试一试,看看效果如何。谢谢! 如果 ContextDecorator 只是 def ... with ... 的语法糖,那么它应该能够访问 func 的参数,对吧?请参阅我的编辑了解更多详细信息。 Python 使用静态作用域; savePen 只能访问定义它的词法范围内的变量,而不是调用它的范围内的变量。 (即便如此,您仍需要 Python 3 的 nonlocal 关键字来修改它们。在这里,这不是问题,因为您只是在引用对象上调用方法。)【参考方案2】:

我通过使用我之前在 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

【讨论】:

以上是关于有没有办法在 ContextDecorator 中访问函数的属性/参数?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在javascript中获取当前时区? [复制]

有没有办法在 compojure 中进行热重载?

有没有办法在 REST API Jersey 中检测预检请求?

有没有办法在一行中填充矢量地图

有没有办法在场景套件中添加轮廓?

有没有办法在 Eclipse 中手动折叠代码?