python, confused in decorate and closure
I have some test code:
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)
when run this code, I got an error shows that 'inputed_num' is not defined
My question is: In wrap function, is there not a closure that func can got 'inputed_num' ?
Anyway, If not, how should I do to got my aim: Initialize some value, and use this value directly in the main function.
Thinks.
No, there isn't a closure like that. Functions can close over variables that are present in the surrounding lexical context, not in the calling context. In other words, if you actually write one function in another, then the inner one can have access to variables in the outer one:
def f():
g = 2
def f2():
print g
f2()
But functions never have access to variables inside the function that called them.
In general there isn't a way to do what you want, viz., set an arbitrary variable in a function from outside the function. The closest thing is you could use a global inputed_num
in your decorator to assign inputed_num
as a global variable. Then test
would access the global value.
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
But of course the variable setting is then global, so multiple concurrent uses (eg, recursion) wouldn't work. (Just for fun, you can also see here for a very arcane way to do this, but it is way too crazy to be useful in a real-world context.)
My question is: In wrap function, is there not a closure that func can got 'inputed_num' ?
Sorry, that's not the way decorators work. They get applied after the function is initially defined. By then, it's too late.
When you write:
@num(5)
def test(a):
return a + inputed_num
That is the equivalent of:
def test(a):
return a + inputed_num
test = num(5)(test) # note that num(5) is called after test() is defined.
To achieve your goal, let inputed_num be the first argument to test. Then, have your decorator pass in that argument:
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
Hope that clears everything up for you :-)
As @Raymond puts it - decorators are applied after the function is defined. Which means that while compiling the function body itself, Pythn sees the inputed_num
variable, and as i it snod a localy defined variable, it generates code to try access it a as a global variable instead.
Which means you can make a work-around for it in your decorator: Your decorator can set a global variable with the desired name in the function globals() space, and then call the function. It should work reliably in single-threaded code:
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
and:
>>> print test(1)
6
链接地址: http://www.djcxy.com/p/28564.html
上一篇: 类方法的python装饰器
下一篇: python,在装饰和关闭时感到困惑