Scope: where names live¶
~15 min ยท prerequisite: Defining and calling functions
What you will be able to do¶
- Explain the difference between a local variable and a global variable.
- Predict whether a name can be read inside a function, and whether a function changes it.
- Pass values into a function and get results back, instead of changing global names.
The idea¶
The scope of a name is the part of a program where that name can be used. Scope answers one question: when Python sees a name, where does it look for the value?
Think of a note written inside a room: it can be read only in that room. In Python, a name created inside a function is a local variable. It exists only while that function is running. A name created at the top level of a file is a global variable. It can be read from anywhere in the file.
A function can read a global variable without any special words. This is why a function can use a setting, such as a tax rate, that is defined once at the top of the file. When Python sees total inside a function, and that function does not create a local total, it looks outward and finds the global one.
Assigning to a name inside a function is different. The assignment total = 5 inside a function creates a new local variable called total. The global total is not touched. The two names only share a spelling.
Python checks names in a fixed order: first the local scope, then any enclosing function scopes, then the global scope, and finally the built-in names such as print and len. This order explains why a local name hides a global name with the same spelling.
If you really want a function to change a global variable, you must say so with the keyword global. It is usually better to pass values in as arguments and return results out.
Worked example¶
total = 100
def add_bonus(amount):
total = amount + 5
return total
result = add_bonus(10)
print(result)
print(total)
Expected output:
Line by line, this is what Python does:
total = 100creates a global variable namedtotal.def add_bonus(amount):creates a function. Its body does not run yet.- Inside the function,
total = amount + 5creates a local variable namedtotal. Becauseamountis 10, the localtotalholds 15. return totalreturns the local value, 15. The function then ends, and its localtotaldisappears.result = add_bonus(10)stores 15 inresult, andprint(result)shows15.print(total)looks at the global name. It was never changed, so it shows100.
Try it¶
The first block shows that a function can read a global name. The second block shows that a local assignment does not change the global. The third block uses global, and then passes a value in instead.
Common mistakes¶
-
Changing a global without
global. The following code tries to add tototalinside the function:Python reports:
The assignment makes
totala local name, so the read on the right side has no value yet. Either addglobal totalor return the new value. -
Using a local name outside its function. The following code reads
textafter the function has ended:Python reports a
NameErrorthat begins with:Depending on the Python version, the message may end with a suggested name, such as
Did you mean.The variable
textexisted only whilemake_messagewas running. Return it, and store the result in a variable. -
Expecting a local change to reach the global. The following check fails:
Python reports:
The function assigned a new local
level. The globallevelis still 1. -
Putting
globalafter the name is used. The following code readscounterbefore thegloballine:Python reports:
Put the
globalline first in the function body.
Practice¶
- Define a global
x = 5, then defineplus_one()that returnsx + 1. Assert thatplus_one()equals6. - Define a global
user = "Ada"and a functionget_user()that returns it. Assert thatget_user()equals"Ada". - Define a global
points = 10and a functionadd_points(amount)that usesglobal pointsand addsamount. Afteradd_points(5), assert thatpointsequals15. - Define
double_all(numbers)that returns a new list with each value doubled, and leaves the list it was given unchanged. Assert thatdouble_all([1, 2])equals[2, 4], and that the original list still equals[1, 2]after the call. - Stretch: define
make_counter()that creates a localcount = 0and returns a functionstep(). Insidestep, usenonlocal countto add 1, then return the new count. Assert that the first counter returns1then2, and that a second counter starts again at1.
Solution
- ```python x = 5
def plus_one(): return x + 1
assert plus_one() == 6 ```
- ```python user = "Ada"
def get_user(): return user
assert get_user() == "Ada" ```
- ```python points = 10
def add_points(amount): global points points = points + amount
add_points(5) assert points == 15 ```
- ```python def double_all(numbers): doubled = [number * 2 for number in numbers] return doubled
original = [1, 2] assert double_all(original) == [2, 4] assert original == [1, 2] ```
-
```python def make_counter(): count = 0
def step(): nonlocal count count = count + 1 return count
return step
first = make_counter() assert first() == 1 assert first() == 2
second = make_counter() assert second() == 1 ```
Recap¶
- A name assigned inside a function is local to that call. Reading a global name from inside a function works.
globallets a function assign to a top-level name, but returning a value is usually clearer.- A local variable is created on each call and disappears when the function returns.