Post
Variable scope and good variable names in Python
Where a variable can be seen and changed in Python: local, global and nonlocal scope, the global and nonlocal keywords, shadowing, and how to choose clear names that follow PEP 8.
The scope of a variable is the part of the program where its name can be used. It is the reason a variable can be “missing” in one function and work fine in another.
Every example ran with Python 3.13, checked against the official tutorial on scopes, the global statement reference, and PEP 8.
Where Python looks for a name
When you use a name, Python searches in this order: the current function (local), then any enclosing functions, then the module (global), and finally the built-in names such as print and len. The docs describe it the same way: “the innermost scope, which is searched first, contains the local names”.
x = "global"
def show_local():
x = "local"
print("inside:", x)
show_local()
print("outside:", x)
inside: local
outside: global
Inside the function, x = "local" creates a new local variable and leaves the global x alone. The docs say that without a global or nonlocal statement, “assignments to names always go into the innermost scope”.
Reading a global is fine
A function can read a global variable without any keyword:
def read_only():
print("read:", x)
read_only()
read: global
Only assigning to the name inside the function makes it local.
The global keyword
To change a global variable from inside a function, declare it:
def with_global():
global x
x = "changed by global"
with_global()
print("now:", x)
now: changed by global
The docs say a declared name is “interpreted as globals”, and all assignments go to the module-level namespace. Use it sparingly: functions that change global state are harder to test and follow, and passing the value in and returning the new one is usually better.
The nonlocal keyword
For a function inside another function, nonlocal changes a variable of the enclosing function:
def outer():
y = "outer"
def inner():
nonlocal y
y = "changed by nonlocal"
inner()
print("outer y:", y)
outer()
outer y: changed by nonlocal
Without nonlocal, inner would create its own y, and outer would print outer.
A name must be declared before use
Inside a function, a variable is local for the whole function body. If you use it before the assignment, you get an error:
def bad_count():
print(total)
total = 5
bad_count()
UnboundLocalError: cannot access local variable 'total' where it is not associated with a value
total is local because of the assignment further down, so the print above looks for a local value that doesn’t exist yet. The same rule applies to global: it must come before any use of the name in that function. Otherwise you get a SyntaxError:
counter = 0
def bump():
counter += 1
global counter
SyntaxError: name 'counter' is assigned to before global declaration
Shadowing a built-in
Scope also explains what happens when you use a built-in name for your own variable:
print(len("abc"))
len = 3
print(len)
3
3
After len = 3, the module-level len hides the built-in len function. Any later call to len(...) in this file fails. This is called shadowing.
Choosing good names
PEP 8 gives the rules for names. The main ones:
- Variables and functions: lowercase, with underscores.
total_price, nottotalPriceorTP. - Constants (values that don’t change, usually at module level): all capitals.
MAX_RETRIES = 3. - Avoid
l,OandI. The docs for PEP 8 say these look like the digits one and zero in some fonts. - Avoid built-in names.
list,str,sum,max,idandlenare common mistakes.
Compare these two versions of the same code:
def f(l, d):
return l * d
def calculate_total_price(unit_price, quantity):
return unit_price * quantity
The second tells the next reader what the numbers mean, which is the whole point of a name. d could be a date, a dictionary or a dog.
Python also has reserved words, which you can’t use as names. class is one of them:
import keyword
print(keyword.iskeyword("class"), keyword.iskeyword("name"))
True False
Trying class = 1 gives SyntaxError: invalid syntax.
What’s next
Next in the series: TypeError, the most common error when a value has the wrong type, and how to check types.