Can a decorated function access decorator variables

I am trying to understand how decorators work, and wondered if a decorated function can access decorator variables. For example, in the following code, how to make f1 access localVariable? Is this possible, and is it even a good way to do something?

def funcDec(func): localVariable = "I'm a local string" def func2Return(*args): print "Calling localVariable from decorator " + localVariable func(*args) print "done with calling f1" return func2Return @funcDec def f1(x, y): print x + y print localVariable f1(2, 3) 
+9
source share
3 answers

No, you can’t. See this previous question . Just because a function is a decorator does not mean that the functions that it calls have special access to their variables. If you do this:

 def func(): a = 2 otherFunc() 

Then otherFunc does not have access to the variable a . This is how it works for all function calls, and how it works for decorators too.

Now the wrapper function that you define inside the decorator ( func2Return in your example) has access to variables, because this function is lexically in the same area as these variables. So your line print "Calling localVariable from decorator " + localVariable will work. You can use this to some extent to wrap a decorated function with behavior that depends on the variables in the decorator. But the actually executed function ( f1 in your example) does not have access to these variables.

The function has access only to local variables from the area where the function definition is actually defined. Functions do not receive variables from calling regions. (It’s good if they did it, it would be a huge mess.)

+4
source

Due to Python scope rules, a decorated function usually cannot access any variables in the decorator. However, since arbitrary attributes can be assigned to functions, you can do something like the following in the decorator to get a similar effect (due to the same scope rules):

 def funcDec(func): localVariable = "I'm a local string" def wrapped(*args): print("Calling localVariable from funcDec " + localVariable) func(*args) print("done with calling f1") wrapped.attrib = localVariable return wrapped @funcDec def f1(x, y): print(x + y) print('f1.attrib: {!r}'.format(f1.attrib)) f1(2, 3) 

Which will produce the following output:

 Calling localVariable from funcDec I'm a local string 5 f1.attrib: "I'm a local string" done with calling f1 

Someone asked if this could be applied to class methods: the answer is yes, but you must refer to the method either through the class itself, or its instance is passed as an argument to self . Both methods are shown below. Using self is preferred because it makes the code independent of the name of the class in which it resides.

 class Test(object): @funcDec def f1(self): print('{}.f1() called'.format(self.__class__.__name__)) print('self.f1.attrib: {!r}'.format(self.f1.attrib)) # Preferred. print('Test.f1.attrib: {!r}'.format(Test.f1.attrib)) # Also works. print() test = Test() test.f1() 

Exit:

 Calling localVariable from funcDec I'm a local string Test.f1() called self.f1.attrib: "I'm a local string" Test.f1.attrib: "I'm a local string" done with calling f1 
+9
source

I think this helps if you remember that the decorator

 @deco def f(...): ... 

is just syntactic sugar for

 def f(...): ... f = deco(f) 

not some kind of macro extension. In Python, the scope of a variable is defined statically, so for a global (modular) function, a variable that is not passed as an argument or not assigned will be viewed in the global namespace.

Therefore, you must explicitly pass the local variable func2Return (). Change the signature of f1 to f1(x, y, localvariable=None) and enable the fun2Return wrapper fun2Return with

 f1(*args, localvariable=localvariable) 
+6
source

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


All Articles