Python 3.5 装饰器和函数字段
Posted
技术标签:
【中文标题】Python 3.5 装饰器和函数字段【英文标题】:Python 3.5 Decorators and function fields 【发布时间】:2019-07-09 14:19:00 【问题描述】:我有以下sn-p的代码:
def wrapper(func):
def wrapped(*args, **kwargs):
func.var = 0
return func(*args, **kwargs)
return wrapped
@wrapper
def f_out():
print(f_out.var)
您能否向我解释一下为什么运行 f_out()
会引发:
AttributeError: 'function' object has no attribute 'var'
编辑
我必须详细说明,因为答案为我提供了替代方案,但这不适用于我想要的情况。给定以下 sn-p:
def wrapper(func):
def wrapped(*args, **kwargs):
func.var = 0
ret = func(*args, **kwargs)
print(func.var)
return wrapped
@wrapper
def f_out():
f_out.var = 1
f_out()
print(f_out.var)
我得到输出:
0
1
为什么会这样?
【问题讨论】:
return func
应该是 return wrapped
。这能解决吗?
@RobinZigmond 你是对的,我会修复它,但是同样的错误
【参考方案1】:
正确的方法是返回被包装的函数,并在返回之前改变它:
def wrapper(func):
def wrapped(*args, **kwargs):
return func(*args, **kwargs)
wrapped.var = 0
return wrapped
@wrapper
def f_out():
print(f_out.var)
你正确得到:
print(f_out())
给予
0
更新后的片段将var
属性更改了两次:
f_out
的函数的属性设置为1。但此时,引用为f_out
的函数是包装函数,不再是原来的函数。
因此,当您稍后打印 f_out.var
时,您会打印包装函数的属性,即 1。
这是一个稍微修改过的代码:
def wrapper(func):
def wrapped(*args, **kwargs):
wrapped.orig = func # keeps a ref to the original function
func.var = 0
ret = func(*args, **kwargs)
print(func.var)
return wrapped
@wrapper
def f_out():
f_out.var = 1
f_out()
print(f_out.var, f_out.orig.var)
打印出来
0
1 0
【讨论】:
这是真的,它有效,但为什么另一个无效?我更新了我的问题 好的,很有趣,谢谢分享,我在其他地方找不到我的问题的答案。我以某种方式理解您关于原始函数和引用函数的论点,但如果是这样,那么对原始函数的对象引用将在包装器中丢失,因此在函数调用后,包装器完全无法访问函数体内修改的字段。 . 这让我问,有没有办法对函数体内的函数字段进行更改,可以通过它的包装器看到,以便例如执行或不执行进一步的计算?【参考方案2】:装饰器在你调用它时包装你的函数。
因此,当您调用f_out()
时,它会返回1
。它是你调用的包装函数,而不是它的定义。
@wrapper
def f_out()
相等
wrapper(f_out)
当你调用它时。
当您尝试打印 f_out.var
时,它会从函数定义中返回值。
【讨论】:
【参考方案3】:如果return func
,你必须添加return wrapped
试试这个:
def wrapper(func):
def wrapped(*args, **kwargs):
func.var = 0
return func(*args, **kwargs)
return wrapped
@wrapper
def f_out():
print(f_out.var)
【讨论】:
如果您尝试拨打f_out()
会发生什么?以上是关于Python 3.5 装饰器和函数字段的主要内容,如果未能解决你的问题,请参考以下文章