Python confused in decorating and closing

I have a 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 you run this code, I got an error: "inputed_num" is not

My question is: In the wrap function, is there a closure that func can get 'inputed_num'?

In any case, if not, how can I do to achieve my goal: Initialize some value and use this value directly in the main function.

Thinks.

+3
source share
4 answers

My question is: in the wrap function, is there a closure that func can get 'inputed_num'?

Sorry, this is not how decorators work. They apply after the function is initially defined. By then it is too late.

When you write:

@num(5) def test(a): return a + inputed_num 

This is the equivalent:

 def test(a): return a + inputed_num test = num(5)(test) # note that num(5) is called after test() is defined. 

To achieve its goal, let inputed_num be the first argument to test. Then let your decorator pass in this 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 

I hope that everything will facilitate you :-)

+5
source

No, there is no such closure. Functions can close variables that are present in the surrounding lexical context, and not in the calling context. In other words, if you are actually writing one function to another, then the inside can have access to the variables in the outside:

 def f(): g = 2 def f2(): print g f2() 

But functions never have access to variables inside the function they call.

In general, there is no way to do what you want, namely, to set an arbitrary variable in a function from outside the function. The closest thing is that you can use global inputed_num in your decorator to assign inputed_num as a global variable. Then test will 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 parameter is global, so multiple concurrent uses (like recursion) will not work. (Just for fun, you can also see here for a very secret way to do this, but it's too crazy to be useful in the real world.)

+5
source

As @ Raymond notes, decorators are applied after defining a function. This means that when compiling the body of the function itself, Pythn sees the variable inputed_num , and since I load a local specific variable, it generates code to try to access it as a global variable.

This means that you can do a workaround for your decorator: Your decorator can set the global variable with the desired name in the globalplaces () function 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 
+2
source

Not the way a decorator should be used, I think your goal can be accomplished with this

 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) 
0
source

Source: https://habr.com/ru/post/925456/


All Articles