Assigning a variable from the parent function: "Local variable specified before assignment"

For the following Python 2.7 code:

#!/usr/bin/python def funcA(): print "funcA" c = 0 def funcB(): c += 3 print "funcB", c def funcC(): print "funcC", c print "c", c funcB() c += 2 funcC() c += 2 funcB() c += 2 funcC() print "end" funcA() 

I get the following error:

 File "./a.py", line 9, in funcB c += 3 UnboundLocalError: local variable 'c' referenced before assignment 

But when I comment out the line c += 3 in funcB , I get the following output:

 funcA c 0 funcB 0 funcC 2 funcB 4 funcC 6 end 

Is c += available in both cases += in funcB and = in funcC ? Why doesn't he throw a mistake for one, but not for the other?

I have no choice to make the global variable c , and then declare global c in funcB . In any case, the point is not to get c incremented in funcB , but for why it throws an error for funcB and not for funcC , while both refer to a variable that is local or global.

+24
python
Jan 19 '12 at 23:13
source share
5 answers

What you see here is the difference between access and variable assignment. In Python 2.x, you can only assign variables in the inner scope or global scope (the latter is done using the global operator). You can access variables in any enclosing scope, but you cannot access a variable in the enclosing scope and then assign it to the innermost or global scope.

This means that if there is any naming within the function, that name should already be defined in the innermost area before the name is accessed (unless the global operator has been used). In your code, line c += 3 is essentially equivalent to the following:

 tmp = c c = tmp + 3 

Since the function has a destination c , any other occurrence of c in this function will be displayed only in the local area for funcB . That's why you see the error, you are trying to access c to get its current value for += , but in the local area c is not defined yet.

In Python 3, you can work around this problem by using a non-local operator that allows you to assign variables that are not in the current scope, but also not globally.

Your code will look something like this, with a similar line at the top of funcC :

  def funcB(): nonlocal c c += 3 ... 

This is not an option in Python 2.x, and the only way to change the value of a non-local variable is to change it.

The easiest way to do this is to wrap your value in a list, and then change and access the first element of this list anywhere you previously used the variable name:

 def funcA(): print "funcA" c = [0] def funcB(): c[0] += 3 print "funcB", c[0] def funcC(): c[0] = 5 print "funcC", c[0] print "c", c[0] funcB() funcC() funcB() funcC() print "end" funcA() 

... and conclusion:

 funcA c 0 funcB 3 funcC 5 funcB 8 funcC 5 end 
+48
Jan 19 '12 at 23:28
source share
— -

Is "c" available in both cases "+ =" in funcB and "=" in funcC?

No, funcC creates a new variable, also called c . = in this respect differs from += .

To get the behavior you (possibly) want, wrap the variable up in a singleton list:

 def outer(): c = [0] def inner(): c[0] = 3 inner() print c[0] 

will print 3 .

Change You want to pass c as an argument. Python 2 has no other way, AFAIK, to get the desired behavior. Python 3 introduces the nonlocal keyword for these cases.

+6
Jan 19 '12 at 23:15
source share

1) Is c += available in both cases += in funcB and = in funcC ?

No, because c += 3 matches:

 c = c + 3 ^ | and funcB does not know what this c is 

2) I have no choice to make c global variable, and then declare global c in funcB .

Please do not do this, just change:

 def funcB(): 

from:

 def funcB(c): 

and call funcB(c) later in your code.

Note. . You must also define funcB and funcC outside of funcA

+2
Jan 19 '12 at 23:31
source share

Try the following:

 def funcA(): print "funcA" c = 0 def funcB(c): c += 3 print "funcB", c def funcC(c): c = 5 print "funcC", c print "c", c funcB(c) funcC(c) funcB(c) funcC(c) print "end" funcA() 

And if you want to remember the value of c, then:

 def funcA(): print "funcA" c = 0 def funcB(c): c += 3 print "funcB", c return c def funcC(c): c = 5 print "funcC", c return c print "c", c c = funcB(c) c = funcC(c) c = funcB(c) c = funcC(c) print "end" funcA() 

which will produce:

 funcA c 0 funcB 3 funcC 5 funcB 8 funcC 5 end C:\Python26\ 
0
Jan 19 '12 at 23:20
source share

Another dirty workaround, which, however, does not require you to make global. Everything is the same, but:

 def funcB(): globals()['c'] += 3 print "funcB", c 
0
Jan 19 '12 at 23:41
source share



All Articles