python,在装饰和闭包中困惑
我有一些测试代码: def num(num): def deco(func): def wrap(*args, **kwargs): inputed_num = num return func(*args, **kwargs) return wrap return deco @num(5) def test(a): return a + inputed_num print test(1) def num(num): def deco(func): def包裹(*args,**kwargs): 输入的数量=数量 返回函数(*args,**kwargs) 回程包装 返回装饰 @数字(5) def测试(a): 返回一个+输入的数值 打印测试(1) 当运行此代码时,我得到一个错误,显示“inputed_num”未定义 我的问题是: 在wrap函数中,func是否可以得到“inputed_num”的闭包 无论如何,如果没有,我应该如何实现我的目标:初始化一些值,并直接在主函数中使用该值。 思考 我的问题是:在wrap函数中,func是否可以得到“inputed_num”的闭包 对不起,装修工不是这样工作的。它们在函数最初定义后应用。到那时,已经太晚了 当你写作时:python,在装饰和闭包中困惑,python,closures,decorator,Python,Closures,Decorator,我有一些测试代码: def num(num): def deco(func): def wrap(*args, **kwargs): inputed_num = num return func(*args, **kwargs) return wrap return deco @num(5) def test(a): return a + inputed_num print test(1)
@num(5)
def test(a):
return a + inputed_num
这相当于:
def test(a):
return a + inputed_num
test = num(5)(test) # note that num(5) is called after test() is defined.
为了实现您的目标,让inputed_num成为第一个要测试的参数。然后,让您的装饰师传入该参数:
def num(num):
def deco(func):
def wrap(*args, **kwargs):
inputed_num = num
return func(inputed_num, *args, **kwargs) # this line changed
return wrap
return deco
@num(5)
def test(inputed_num, a): # this line changed
return a + inputed_num
@num(6)
def test2(inputed_num, a):
return a + inputed_num
print test(10) # outputs 15
print test2(10) # outputs 16
希望这能为你清理一切:-)不,没有那样的结局。函数可以关闭周围词法上下文中的变量,而不是调用上下文中的变量。换句话说,如果您实际在另一个函数中写入一个函数,那么内部函数可以访问外部函数中的变量:
def f():
g = 2
def f2():
print g
f2()
但是函数永远无法访问调用它们的函数内部的变量
一般来说,没有一种方法可以实现您想要的功能,即从函数外部设置函数中的任意变量。最接近的是,您可以在装饰器中使用global inputed_num
将inputed_num
指定为全局变量。然后test
将访问全局值
def num(num):
def deco(func):
def wrap(*args, **kwargs):
global outsider
outsider = num
return func(*args, **kwargs)
return wrap
return deco
@num(5)
def test(a):
print a+outsider
>>> test(2)
7
当然,变量设置是全局的,因此多个并发使用(例如递归)将不起作用。(只是为了好玩,你也可以用一种非常神秘的方法来做这件事,但它太疯狂了,在现实世界中没有用处。)这不是装饰器应该使用的方式,我想你可以用它来达到目的
def outwrapper(n):
def wrapper(func):
def f(*args, **argw):
return n + func(*args, **argw)
return f
return wrapper
@outwrapper(4)
def test(n):
return n
print test(1)
正如@Raymond所说的那样,定义函数后应用装饰器。这意味着,在编译函数体本身时,Pythn会看到
inputed_num
变量,当它不是本地定义的变量时,它会生成代码以尝试将其作为全局变量访问
这意味着您可以在您的装饰师中为其进行变通:
您的装饰程序可以在函数globals()空间中设置具有所需名称的全局变量,然后调用该函数。它应在单线程代码中可靠工作:
def num(num):
def deco(func):
def wrap(*args, **kwargs):
glob = func.func_globals
marker = object()
original_value = glob.get("inputed_num", marker)
glob["inputed_num"] = num
result = func(*args, **kwargs)
if original_value is marker:
del glob["inputed_num"]
else:
glob["inputed_num"] = original_value
return result
return wrap
return deco
@num(5)
def test(a):
return a + inputed_num
以及:
定义函数后应用装饰程序并不一定意味着已经太迟了。直到函数真正被调用时才会引发错误,而且由于调用的是修饰的函数,因此它可以在调用时操纵全局变量来改变原始函数的行为。OP没有要求全局变量——这会有它自己的问题(特别是,如果他多次使用修饰符)。相反,他想要一个闭包(也称为单元格变量)。Python的语义要求在最初定义test()时静态引用单元格变量(词汇范围)。当装饰程序在之后运行时,创建单元格变量实在太晚了。读了你的帖子后,我明白了我的错误。但我不想做一些主要的修改func@RaymondHettinger:是的,OP确实使用了“闭包”一词,但文章的结尾表明他更普遍地在寻找一种从外部修改函数变量的方法,无论是通过闭包还是其他方式。@RaymondHettinger抱歉,还是问题。在wrap中,我添加了以下代码:
如果args[0]==1:return 0
在return func(*args,**kwargs)
之前,然后调用test(1)
,结果是0。这意味着在调用目标函数之前,decoration已经调用并正在运行。不是吗?代码只是一个例子。但还是要谢谢:)酷!但也许不能满足我的需要。这种装饰将提供不同的功能,并同时运行。Thinks:)认为,正如您所提到的,这段代码确实是并发运行的。:)使用一个全局变量在多个不同的函数之间进行通信远非最佳实践。除非没有别的办法,否则我不推荐它。
>>> print test(1)
6