Python nested functions variable scoping [duplicate]
In Python 3, you can use the nonlocal
statement to access non-local, non-global scopes.
The nonlocal
statement causes a variable definition to bind to a previously created variable in the nearest scope. Here are some examples to illustrate:
def sum_list_items(_list): total = 0 def do_the_sum(_list): for i in _list: total += i do_the_sum(_list) return totalsum_list_items([1, 2, 3])
The above example will fail with the error: UnboundLocalError: local variable 'total' referenced before assignment
Using nonlocal
we can get the code to work:
def sum_list_items(_list): total = 0 def do_the_sum(_list): # Define the total variable as non-local, causing it to bind # to the nearest non-global variable also called total. nonlocal total for i in _list: total += i do_the_sum(_list) return totalsum_list_items([1, 2, 3])
But what does "nearest" mean? Here is another example:
def sum_list_items(_list): total = 0 def do_the_sum(_list): # The nonlocal total binds to this variable. total = 0 def do_core_computations(_list): # Define the total variable as non-local, causing it to bind # to the nearest non-global variable also called total. nonlocal total for i in _list: total += i do_core_computations(_list) do_the_sum(_list) return totalsum_list_items([1, 2, 3])
In the above example, total
will bind to the variable defined inside the do_the_sum
function, and not the outer variable defined in the sum_list_items
function, so the code will return 0
.
def sum_list_items(_list): # The nonlocal total binds to this variable. total = 0 def do_the_sum(_list): def do_core_computations(_list): # Define the total variable as non-local, causing it to bind # to the nearest non-global variable also called total. nonlocal total for i in _list: total += i do_core_computations(_list) do_the_sum(_list) return totalsum_list_items([1, 2, 3])
In the above example the nonlocal assignment traverses up two levels before it locates the total
variable that is local to sum_list_items
.
Here's an illustration that gets to the essence of David's answer.
def outer(): a = 0 b = 1 def inner(): print a print b #b = 4 inner()outer()
With the statement b = 4
commented out, this code outputs 0 1
, just what you'd expect.
But if you uncomment that line, on the line print b
, you get the error
UnboundLocalError: local variable 'b' referenced before assignment
It seems mysterious that the presence of b = 4
might somehow make b
disappear on the lines that precede it. But the text David quotes explains why: during static analysis, the interpreter determines that b is assigned to in inner
, and that it is therefore a local variable of inner
. The print line attempts to print the b
in that inner scope before it has been assigned.
When I run your code I get this error:
UnboundLocalError: local variable '_total' referenced before assignment
This problem is caused by this line:
_total += PRICE_RANGES[key][0]
The documentation about Scopes and Namespaces says this:
A special quirk of Python is that – if no
global
statement is in effect – assignments to names always go into the innermost scope. Assignments do not copy data — they just bind names to objects.
So since the line is effectively saying:
_total = _total + PRICE_RANGES[key][0]
it creates _total
in the namespace of recurse()
. Since _total
is then new and unassigned you can't use it in the addition.