Actually I want to initialize because the iteration range is different. and Sorry I was asking if there is no function, i.e., I just want to run the below code
iter = 0 for i in 1:10 global iter +=1 endThis kind of “variable lifetime” is known as scoping. After reading this article, you will know the scoping rules of Python. Let’s start!
The 3 Scopes of PythonPython has 3 scopes:
- Global: In the main part of the script. By default, this already contains the built-ins. You can access all global variables with Traceback (most recent call last):
File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment7 - Enclosed: In the outer function, if this is a nested function
- Local: Within the current function. You can access all local variables with Traceback (most recent call last):
File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment8 . Within the main script, Traceback (most recent call last):
File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment9
You can see all three in action here:
There is a crucial point here:
The scope of a variable is defined at compile-time!
For this reason, the following code throws an exception:
def foo():print(min([1, 2, 3]))
min = lambda n: "local"foo()
throws the exception:
Traceback (most recent call last):File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment
The “global” statement
You can assign a value to a global variable in a function with the global statement:
x = "global"def foo():global x
x = "local"foo()
print(x) # gives "local"
I need this very rarely. To make sure that I don’t confuse anything, I like to use the Traceback (most recent call last):
File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment7 dictionary. In this case, I would rather use:
globals()["x"] = "local"foo()
print(x) # gives "local"
The “nonlocal” statement
You can assign a value to an enclosed variable with the nonlocal statement:
x = "global"def foo():x = "enclosed" def bar():
nonlocal x
x = "local" bar()
print(x) # gives "local"foo()
print(x) # gives "local"
Confusing Examples
Image by wokandapix (source)
Append element vs ‘+= [element]’
xs = []def foo():xs.append(42) # OKfoo()
vs
xs = []def foo():xs +=[42] # UnboundLocalError: local variable 'xs'
# referenced before assignmentfoo()
The reason why the first works but not the second is that the first one calls a function of xs. It never assigns a value to xs. The second one is equal to
xs = xs + [42]foo()
When Python parses the assignment x = "global"def foo():
global x
x = "local"foo()
print(x) # gives "local"1 , the x = "global"def foo():
global x
x = "local"foo()
print(x) # gives "local"2 is assigned the local scope. But in the local scope, x = "global"def foo():
global x
x = "local"foo()
print(x) # gives "local"2 does not exist before x = "global"def foo():
global x
x = "local"foo()
print(x) # gives "local"4 is executed. Hence the error.
In the first example with x = "global"def foo():
global x
x = "local"foo()
print(x) # gives "local"5 , the global scope of x = "global"def foo():
global x
x = "local"foo()
print(x) # gives "local"2 is used. Hence we don’t face any issue, because it is defined in the global scope.
Global scope DOES fall back to built-ins
# prints 1print(min([1, 2, 3]))
min = lambda n: "local"
but the same does not work in a local scope
# UnboundLocalError: local variable 'min' referenced before assignmentdef foo():
print(min([1, 2, 3]))
min = lambda n: "local"foo()
The reason is that Traceback (most recent call last):
File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment9 within the global scope. Although built-ins are a bit special, they kind of live in the global scope.
Assignment
This one is confusing to people with a Java, C, or C++ background. This is valid Python code:
Traceback (most recent call last):File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment0
But in Java you need to declare it upfront to be able to use the variable after the loop:
Traceback (most recent call last):File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment1
mypy
mypy is a wide-spread type-checker for Python.
Traceback (most recent call last):File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment2
So mypy doesn’t like that you assign the string “foo” to x = "global"def foo():
global x
x = "local"foo()
print(x) # gives "local"8 , because it first read took the x = "global"def foo():
global x
x = "local"foo()
print(x) # gives "local"9 path and assumed that x = "global"def foo():
global x
x = "local"foo()
print(x) # gives "local"8 would be an integer.
Then you might want to do this:
Traceback (most recent call last):File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment3
You can see that mypy assumes the x = "global"def foo():
global x
x = "local"foo()
print(x) # gives "local"8 should be the same type in both cases. It’s reasonable because otherwise the following analysis might get extremely complicated.
The next try might be:
Traceback (most recent call last):File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment4
That should have been expected. Assigning x = "global"def foo():
globals()["x"] = "local"foo()
print(x) # gives "local"2 is not possible to a type which does not include x = "global"def foo():
globals()["x"] = "local"foo()
print(x) # gives "local"2 — and adding x = "global"def foo():
globals()["x"] = "local"foo()
print(x) # gives "local"2 to that type might cause many more issues down the road.
You can do this:
Traceback (most recent call last):File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment5
But I can also see when this feels strange. What might be cleaner is this:
Traceback (most recent call last):File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment6
Summary
We have seen the three types of scopes in Python: Local, Enclosed, and global. We’ve seen that you can access globals with the x = "global"def foo():
globals()["x"] = "local"foo()
print(x) # gives "local"5 keyword or over the Traceback (most recent call last):
File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment7 dictionary. Locals can be accessed with the Traceback (most recent call last):
File "example.py", line 5, in <module>
foo()
File "example.py", line 2, in foo
print(min([1, 2, 3]))
UnboundLocalError: local variable 'min' referenced before assignment8 dictionary and enclosed variables with the x = "global"def foo():
globals()["x"] = "local"foo()
print(x) # gives "local"8 keyword. Keep that in mind and Python scoping should make sense 🙂