Because of Python's scoping rules, a decorated function generally can't access any variables in the decorator. However, since functions can have arbitrary attributes assigned to them, you could do something like the following in the decorator to get a similar effect (due to the same scoping 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 would 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 whether this could be applied to methods of a class:
The answer is "yes", but you have to reference the method either through the class itself or the instance of it passed as the self
argument. Both techniques are shown below. Using self
is preferable since it makes the code independent of the name of the class it's is in.
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()
Output:
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
Update
Another way of doing this that would give the decorated function more direct access to decorator variables would be to temporarily "inject" them into its global namespace (and then remove them afterwards).
I got the idea from @Martijn Pieters' answer to the somewhat related question: How to inject variable into scope with a decorator?
def funcDec(func):
localVariable = "I'm a local string"
# Local variable(s) to inject into wrapped func.
context = {'localVariable': localVariable}
def wrapped(*args):
func_globals = func.__globals__
# Save copy of any global values that will be replaced.
saved_values = {key: func_globals[key] for key in context if key in func_globals}
func_globals.update(context)
print(f'Calling localVariable from funcDec: {localVariable!r}')
try:
func(*args)
finally:
func_globals.update(saved_values) # Restore any replaced globals.
print(f'done with calling {func.__name__}()')
return wrapped
@funcDec
def f1(x, y):
print(x + y)
print(f'Calling funcDec localVariable from f1: {localVariable!r}')
f1(2, 3)
Result from this version:
Calling localVariable from funcDec: "I'm a local string"
5
Calling funcDec localVariable from f1: "I'm a local string"
done with calling f1()