This question already has an answer here:
I've read almost all the other questions about the topic, but my code still doesn't work.
I think I'm missing something about python variable scope.
Here is my code:
PRICE_RANGES = {
64:(25, 0.35),
32:(13, 0.40),
16:(7, 0.45),
8:(4, 0.5)
}
def get_order_total(quantity):
global PRICE_RANGES
_total = 0
_i = PRICE_RANGES.iterkeys()
def recurse(_i):
try:
key = _i.next()
if quantity % key != quantity:
_total += PRICE_RANGES[key][0]
return recurse(_i)
except StopIteration:
return (key, quantity % key)
res = recurse(_i)
And I get
"global name '_total' is not defined"
I know the problem is on the _total
assignment, but I can't understand why.
Shouldn't recurse()
have access to the parent function's variables?
Can someone explain to me what I'm missing about python variable scope?
You probably have gotten the answer to your question. But i wanted to indicate a way i ussually get around this and that is by using lists. For instance, if i want to do this:
I would instead do this:
This way X is never a local variable
More from a philosophical point of view, one answer might be "if you're having namespace problems, give it a namespace of its very own!"
Providing it in its own class not only allows you to encapsulate the problem but also makes testing easier, eliminates those pesky globals, and reduces the need to shovel variables around between various top-level functions (doubtless there'll be more than just
get_order_total
).Preserving the OP's code to focus on the essential change,
As a PS, one hack which is a variant on the list idea in another answer, but perhaps clearer,
When I run your code I get this error:
This problem is caused by this line:
The documentation about Scopes and Namespaces says this:
So since the line is effectively saying:
it creates
_total
in the namespace ofrecurse()
. Since_total
is then new and unassigned you can't use it in the addition.In Python 3, you can use the
nonlocal
statement to access non-local, non-global scopes.This is a variation of redman's solution, but using a proper namespace instead of an array to encapsulate the variable:
I'm not sure if using a class object this way is considered an ugly hack or a proper coding technique in the python community, but it works fine in python 2.x and 3.x (tested with 2.7.3 and 3.2.3). I'm also unsure about the run-time efficiency of this solution.
While I used to use @redman's list-based approach, it's not optimal in terms of readability.
Here is a modified @Hans' approach, except I use an attribute of the inner function, rather than the outer. This should be more compatible with recursion, and maybe even multithreading:
This prints:
If I
s/inner.attribute/outer.attribute/g
, we get:So, indeed, it seems better to make them the inner function's attributes.
Also, it seems sensible in terms of readability: because then the variable conceptually relates to the inner function, and this notation reminds the reader that the variable is shared between the scopes of the inner and the outer functions. A slight downside for the readability is that the
inner.attribute
may only be set syntactically after thedef inner(): ...
.