【问题标题】:Is there a way to access a function's attributes/parameters within a ContextDecorator?有没有办法在 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)

【问题讨论】:

    标签: python python-3.x python-decorators contextmanager


    【解决方案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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-01
      • 1970-01-01
      • 2021-10-30
      • 2022-08-02
      • 1970-01-01
      • 2022-08-23
      • 2021-02-04
      • 1970-01-01
      相关资源
      最近更新 更多