UnboundLocalError with nested function scopes
If you're using Python 3, you can use the nonlocal
statement to enable rebinding of a nonlocal name:
def outer():
ctr = 0
def inner():
nonlocal ctr
ctr += 1
inner()
If you're using Python 2, which doesn't have nonlocal
, you need to perform your incrementing without barename rebinding (by keeping the counter as an item or attribute of some barename, not as a barename itself). For example:
...
ctr = [0]
def inner():
ctr[0] += 1
...
and of course use ctr[0]
wherever you're using bare ctr
now elsewhere.
Nested function definitions and scope (UnboundLocalError)
Python needs first to detect what variables are local, and which variable are fetched from an outer scope. In order to do that it looks for assignments, like:
def foo1(x=5):
def bar():
if x == 5:
x = 6 # an assignment, so local variable
print(x)
bar()
The point is, that the assignment can happen anywhere. For instance on the last line. Nevertheless, from the moment there is an assignment somewhere x
is local. So in your first code fragment, x
is a local variable. But you fetch it before it is assigned (bounded), so Python will error on it.
In python-3.x you can use the nonlocal
keyword to access x
from an outer scope:
def foo1(x=5):
def bar():
nonlocal x
if x == 5:
x = 6
print(x)
bar()
For python-2.x, you can for instance assign the variable to the function, like:
def foo1(x=5):
def bar():
if bar.x == 5:
bar.x = 6
print(bar.x)
bar.x = x
bar()
Note however that the two are not equivalent. Since in the former if you alter x
, it will be alter the x
in the foo1
scope as well. In the latter example you only modify bar.x
. Of course if these are mutable objects, you alter the same object.
Nested Function Scope
I don't think the other answers narrate the entire story, so here's my 2 cents.
The scope does not differ between types. That's not what's happening here.
Rebinding a name, regardless of what that name refers to, will always cause an unbound local error to occur.
Some types in Python are mutable (once created, a value can be changed), and list is one of them. Some are immutable, and changing a value of these types requires creating a new object.
So, with the first example,
def foo():
a = 0
def bar():
a += 1
bar()
foo()
This doesn't work because you effectively have an assignment taking place here, a = a + 1
. You can use non-local
to make the above work, that being besides the point.
Doing the same thing with a list:
def foo():
a = []
def bar():
a.append(1)
bar()
foo()
This does indeed work. There is no assignment taking place here.
You can't re-bind the name to a different object, but if the object is mutable, you can modify its contents.
Now, there's 2 more cases you should be aware of.
def foo():
a = []
c = []
def bar():
a = c + [1]
bar()
print(a)
print(c)
foo()
This will work, however you should note that the a
inside bar()
now is local to bar, and the print() statement should reflect that.
But here's a gotcha
def foo():
a = []
def bar():
a = a + [1] #or even a += [1], doesn't matter
bar()
print(a)
foo()
This won't work! (And it's important you contrast this snippet with the first snippet, because that addresses why this has nothing to do with scopes. Take a minute to read it again.)
So this doesn't work and it's important to understand it.
If there is an assignment to a variable inside a function, that variable is considered local.
Now, in the last case, when we did a = c + 1
, it was more like a_local = c_nonlocal + 1
.
In this case, a = a + 1
is a_local = a_local + 1
and hence, that will indeed cause an error.
This is why, Rebinding a name, regardless of what that name refers to, will always cause an unbound local error to occur.
In the case earlier(The third snippet), it wasn't rebinding it - it was creating a local variable.
In the latter case(The fourth snippet), it was infact rebinding and hence the error.
Python 3 parameter closed upon by inner/nested method falls out of scope and triggers UnboundLocalError
The problem is that you reference uuidString in the call to self.getMethodThatProvidesFullFilePathNameForPricesCsvFromUUIDAndTickerName
before you assign to it. The assignment makes it local to the scope of the innermost function and therefore, it is unassigned when you reference it.
A full description of the scoping rules is provided by: https://stackoverflow.com/a/292502/7517724
This simpler example reproduces your error to make the problem more clear:
class aclass():
def outer(self, uuidString):
def inner():
print(uuidString)
uuidString = 'new value'
return uuidString
return inner
a = aclass()
func = a.outer('a uuid')
val = func()
print(val)
The assignment in inner()
causes the uuidString
to be local to inner()
and therefore it is unassigned when the print(uuidString)
is call, which causes Python to raise the UnboundLocalError
.
You can fix the error by passing the variable in to your function with a default argument. Changing the definition of saveData
to pass uuidString
as a default argument, as:
def saveData(uuidString=uuidString):
will make it work as you expect.
Python nested functions variable scoping
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.
UnboundLocalError in recursive call of nested function
Because you're assigning results inside of a nested function, Python assumes you're using a locally scoped variable and throws up at line 35 even though it's a valid name in a higher scope. If you were only reading the variable and not writing to it, it will oftentimes work on the higher namespace object. But as soon as an assignment operator appears, you jump to local namespace.
From Python scopes/namespaces:
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.
To get around this, the easiest is to pass the variable you want use into the function header:
def extend_prefix(w, letters, results):
if w in WORDS: results.add(w)
if w not in PREFIXES: return
for L in letters:
extend_prefix(w + L, letters.replace(L, "", 1), results)
Also the way you wrote the function, you weren't returning a set, so the results = results | result
would have blown up with results being None Type.
UnboundLocalError with nested function when modifying parent function argument
The second chunk of code fails because hi
is a parameter in the scope of the hello
function, yet it is assigned again in the world
function
How do I change global variable in nested scope using python?
The swaps
variable in your code is not global, it's nonlocal. It was defined in the enclosing function getNumOfSwapsToSort()
, not in the global namespace.
Your other function, swap()
, has access to this nonlocal variable, but the variable is protected from being edited from the inner function. If you try to edit or overwrite it, it's going to be treated as a new, local variable - hence the UnboundLocalError you're getting.
Try using the nonlocal
statement:
nonlocal swaps
swaps += 1
Here's an article with a few simple examples which explain the concept of closure in Python.
Related Topics
Assign Environment Variables from Bash Script to Current Session from Python
Priority of the Logical Operators Not, And, or in Python
How to Pass a List as a Command-Line Argument with Argparse
Converting Int to Bytes in Python 3
Automatically Create Requirements.Txt
Recursion and Return Statements
Reading an Excel File in Python Using Pandas
Changing the Formatting of a Datetime Axis in Matplotlib
Pandas Dataframe Groupby Two Columns and Get Counts
The Correct Cmakelists.Txt File to Call a Maxon Libarary in a Python Script Using Pybind11
Calling Java/Scala Function from a Task
Parsing Boolean Values with Argparse
Importing Variables from Another File
What's the How to Install Pip, Virtualenv, and Distribute for Python
Is There a Decorator to Simply Cache Function Return Values
Why Isn't Pycharm's Autocomplete Working for Libraries I Install