So I need some repeated actions within a large function. Aha! nested functions to the rescue! Oh, but most of the repetition involves modification to local variables in the function! Aha! nonlocal to the rescue! But then there is nearly as much nonlocal statement as nested function content. What I really need is a macro?
Hmm. nonlocal * (meaning all variables would be nonlocal references) would be nice, then the nested function could have all its references to the outer scope... but that wouldn't be restricted to the just-outer scope, which could be bad as a general technique. Oh yes, and nonlocal * doesn't exist.
What to do? Hmm. Instead of def, how about compile() some code, and exec() it later?
nestedfunc = compile("some code", "nestedfunc", "exec")
so then later
exec( nestedfunc )
but what about this note from the documentaiton?
Note: The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns.
"some code" really wants to modify local variables in the current scope. Is this going to work?
Nope, even a simple case like some code being
y=y+1
demonstrates the validity of the warning: y is left unchanged in future uses.
Hmm. What if the outer function were also a block of compile'd code, and the locals were passed in to it? That seems to work with cursory testing, the value of y increases with each invocation of the nested function from the compile'd outer function.
a_global = 10
outer_func = compile('''
print( f'{a_global}, {y}')
exec( nested_func )
print( f'{a_global}, {y}')
exec( nested_func )
print( f'{a_global}, {y}')
exec( nested_func )
print( f'{a_global}, {y}')
exec( nested_func )
print( f'{a_global}, {y}')
''', 'outer_func', 'exec')
nested_func = compile('''
global a_global
a_global += 10
y += 1
''', 'nested_func', 'exec')
locs = {'y': 1 }
exec( outer_func, globals(), locs )
exec( outer_func, globals(), locs )
exec( outer_func, globals(), locs )
results:
10, 1
20, 2
30, 3
40, 4
50, 5
50, 5
60, 6
70, 7
80, 8
90, 9
90, 9
100, 10
110, 11
120, 12
130, 13
So this code seems to meet the requirements: nested_func can reference and update local variables in outer_func without neending nonlocal or nonlocal *, cannot (except by using nonlocal or global) access variables in other outer scopes, and is defined only in one place for consistent updates.
Sure is ugly, though. Does anyone see any holes, or have a better solution?