Hi EarthBotV2,
You can’t use a nonlocal variable if there is not a nonlocal variable to
bind to.
This is a little complicated, so bear with me. Globals and nonlocals
behave slightly differently. When you declare a global, Python doesn’t
care whether it already exists or not.
But when you declare a nonlocal, it must already exist. That means that
nonlocal
declarations can only work inside a nested function:
def outer():
a = None # The *local* variable a must exist.
def inner():
nonlocal a
# nonlocal must be inside the inner function, not the
# outer function
...
In fact, I’m surprised that you don’t get a syntax error saying that the
nonlocal declaration is illegal in a non-nested function.
The reason for this difference is a bit complicated, but basically,
global variables live inside a dict and can be created and destroyed
dynamically, but nonlocals live inside a closure and a storage slot must
be pre-allocated for them. And a closure is a snapshot of the
environment inside a running function, so if there is no running
function, there is no closure and you cannot have a nonlocal.
Anyway, the bottom line is that nonlocals can only be used inside nested
functions, and they always refer to a variable in one of the surrounding
functions.
Let’s put eval aside for now. Can you show us what you are trying to do
inside a comprehension, without assuming any mechanism, and we may be
able to suggest a way to do it that may or may not involve eval.
I’m afraid your sample code confuses me:
# Module M:
if True:
# r = 1
def F( aEval=[]):
# nonlocal r
def G():
pass
for tChar in 'abc':
if tChar == 'b':
r = 7
tEval = aEval[ 0]
print( [ f'E({ eval( tEval)})' for n in range( 3)]) # List comprehension: eval now returns -1!
F( ['r-2'])
Some questions:
-
Why do you have an unnecessary if True
branch, since True is always
true, the if
block will always run;
-
Why do you pass the string you want to evaluate inside a list?
-
Why does the parameter have a default value that isn’t used?
-
Are you aware that default mutable arguments may cause confusion?
-
What is the purpose of the do-nothing G function?
-
What’s the purpose of the inner for-loop?
-
What’s E
?
-
What’s the purpose of the f-string, instead of just calling eval
directly?
I think that this might demonstrate your issue in a more straight-
forward way:
r = 1
def F(expression):
t = 'b'
r = 7
print([ eval(expression) for i in range(1) ])
F('r - 1')
Which prints [0]
, but I think you are expecting to print [6]
.
Correct?
Tell us what you are trying to do, and we’ll see what we can do to help.