Suppose I have a function, which has a large section of code repeated in various places within the function, I can do the following:
def foo():
def bar():
# do some stuff
bar()
# do some other stuff
bar()
I can ‘read’ the variables that are in the scope of foo whilst inside bar, furthermore if I need to edit them, I can do this:
def foo():
# stuff involving var1 and var2
def bar():
nonlocal var1, var2
# do some stuff
bar()
# do some other stuff
bar()
The Problem
Now suppose I have several functions, foo_1, foo_2, foo_3… etc, all of which have the same lines of code from bar repeated in them. It would be monotonous (not to mention a nightmare every time I wanted to change bar) to define bar inside each foo_i, however doing the following does not work, since it appears nonlocal works on the scope in which a function is defined, not in which it is called:
def bar():
nonlocal var1, var2 # SyntaxError: no binding for nonlocal 'var1' found
# do some stuff
def foo_1():
# stuff involving var1 and var2
bar()
# do some other stuff
bar()
A potential solution
One way round this problem it to pass in all of the variables that you need to change, and then return them afterwards. Something like this:
def bar(var1, var2):
# do some stuff
return var1, var2
def foo_1():
# stuff involving var1 and var2
var1, var2 = bar(var1, var2)
# do some other stuff
var1, var2 = bar(var1, var2)
My Question
The solution above has a few problems:
bar() (especially when there are more variables)bar inside every foo_i since suppose a variable I previously just accessed within bar I now decide to edit. Not only do I need to change the function, but I need to change everywhere it is called (since now I must return an extra variable).Is there a better way of achieving the above?
(This feels like the sort of problem that aught to have an answer, so I apologise if it’s a repeated question. I haven’t been able to find anything as yet though.)
[...] it appears nonlocal works on the scope in which a function is defined, not in which it is called [...].
You are correct. nonlocal only applies to the namespace in which said function is defined. From the documentation for nonlocal:
The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals.
(emphasis mine)
As @Aran mentioned, a potential solution to avoid unnecessary verbosity is to wrap the variables you want to pass into certain functions in a class. namedtuple would be a attractive choice since you don't need a full-fledge class, but as has been stated, it is immutable. You can use a types.SimpleNamespace object instead, as they are also lightweight:
from types import SimpleNamespace
def bar(n):
n.a = 3, n.b = 4
def foo():
n = SimpleNamespace(a=1, b=2)
bar(n)
print(n.a, n.b) # 3 4
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With