有没有办法在 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()
内部的任何运行都可以访问函数参数 dc
、param1
和 param2
。这让我觉得我应该能够使用 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 中访问函数的属性/参数?的主要内容,如果未能解决你的问题,请参考以下文章