Programming Fundamentals
Scope describes how far a variable's name reaches through a program: it is the region of code where that name exists, so you can read its value there and, where the language allows, assign a new one. Outside that region the name means nothing to the program, so it cannot be printed, used in a calculation or changed there.
Scope follows from where a variable is created. Every variable in Python is either local or global, and the two words simply describe how far its name reaches.
| Local variable | Global variable | |
|---|---|---|
| Created | inside a sub program | in the main program, outside every sub program |
| Can be used in | that one sub program only | the whole program, including every sub program |
The two scopes are separate, and that is what makes them useful. The main program cannot see a variable that belongs to a sub program, and one sub program cannot see another's variables, so each sub program keeps its working values to itself.
Scope is a general idea in programming, not something special to Python: any high-level language decides which names are visible where. You apply the idea in Python, where it explains how a program behaves.
Constants have scope in exactly the same way, so a constant can be local to one sub program or global to the whole program.
The program below shows scope in action. The value laps lives and dies with the sub program that made it, so the last line cannot reach it.
def count_laps():
laps = 6 # created here, so its scope is this sub program
print(laps)
count_laps()
print(laps) # the main program is outside the scope of lapsOutput:
6
NameError: name 'laps' is not defined (line 6)
Working out a name's scope is what lets you predict what a program will print.
A local variable is a variable that belongs to one sub program. It is created inside that sub program, either by an assignment in its body or by being named in its parameter list. It can be used anywhere inside its own sub program, but nowhere else. In Python, that means a name first assigned inside the indented body of a def block, or listed in the brackets.
A local variable only exists while its sub program is executing. Once that sub program finishes, the variable is gone. It does not keep its value between calls: each call starts again with a fresh variable. If a sub program finishes with a local variable holding 7, the next call does not begin with 7 — the old variable has gone, so the sub program must create it again before it can be used again.
The main program cannot use it, and neither can any other sub program. Trying to use it outside its own sub program is an error, and the program stops rather than carrying on with a missing value. A parameter is local in the same way: it belongs to that one sub program and cannot be reached from outside. In the program below, cup_size belongs to make_smoothie, so the last line has no such variable to find.
def make_smoothie(fruit):
cup_size = "large"
print(fruit, "smoothie in a", cup_size, "cup")
make_smoothie("mango")
print(cup_size)Output:
mango smoothie in a large cup
NameError: name 'cup_size' is not defined (line 6)
Two different sub programs may each contain a local variable with the same name. These are completely separate variables held in separate areas of memory, so changing one has no effect at all on the other, even though the code in the two sub programs looks similar. Here, each sub program has its own local badge.
def make_badge(first_name):
badge = "Visitor: " + first_name
print(badge)
def make_ticket(last_name):
badge = "Ticket: " + last_name
print(badge)
make_badge("Zara")
make_ticket("Okafor")Output:
Visitor: Zara
Ticket: OkaforA global variable is a variable created in the main program, outside every sub program definition, usually by an assignment near the top. Because it belongs to the whole program rather than to a single sub program, every part of the program is able to reach it.
Its scope is the whole program, so both the main program and every sub program can read its value. A procedure can access a global variable with no special statement or declaration and with nothing passed in: Python looks for a local variable first, finds none, and uses the global one instead. The variable also stays in existence for as long as the program runs, so its value does not vanish when a sub program finishes.
bottles_collected = 24 # created in the main program
def report_progress():
print("Bottles so far: " + str(bottles_collected))
report_progress()
print("Final total: " + str(bottles_collected))Output:
Bottles so far: 24
Final total: 24
The procedure reads bottles_collected directly, and so does the main program, even though nothing was passed in and the procedure contains no declaration of its own.
Globals suit a value that different sections of the program genuinely need: a shared running total, a setting that several sub programs must follow, or a shared target that more than one sub program checks. The value is set in one place, so there is only one copy to keep correct, and any sub program can use it straight away without it being passed in.
Reading a global and changing one are not the same, though: reading needs nothing special, but assigning a new value to a global from inside a sub program needs a global statement naming that variable.
The global statement tells Python that a name used inside a sub program means the single shared global variable, not a new local one. You need one in any sub program that assigns a new value to a global variable.
Put it at the start of the sub program body, naming the variable it is about.
fuel = 40
def refuel():
global fuel
fuel = fuel + 15
refuel()
print(fuel)Output:
55Because of global fuel, the assignment inside refuel changes the one global fuel. The new value is still there after the sub program ends, so the main program prints the updated total.
The statement does not give the variable a value — the global already exists, created in the main program. It only says which variable the name refers to here. The line also affects nothing outside the sub program it is written in, so every sub program that assigns to fuel needs its own global line. A sub program that only reads the value needs no statement at all, because Python falls back to the global when there is no local of that name.
Python treats any name assigned inside a sub program as a new local variable. Assigning to it without a global statement therefore creates a separate local with that name, and the global keeps its original value — printing in the main program afterwards shows the value from before the call.
| Sub program contains | What the assignment does |
|---|---|
global fuel | Changes the shared global; the new value stays after the sub program ends |
| No global statement | Creates a new local variable; the global is unchanged |
Python decides what a name means by looking at the whole sub program, not just the line it sits on. If the sub program reads the name before the line that assigns to it, and there is no global statement, the program stops with an error: the name is a local variable that has not yet been given a value.
tickets = 12
def print_tickets():
print(tickets)
tickets = 0
print_tickets()Output:
UnboundLocalError: cannot access local variable 'tickets' where it is not associated with a value (line 4)Sometimes the main program and a sub program both use the same variable name, and the sub program assigns a value to it. Which variable the name means inside the sub program depends on one thing: whether the sub program contains a global statement for that name.
When the sub program assigns to the name without a global statement, Python creates a new local variable with that name. Every use of the name inside that sub program then refers to the local variable, not the global one. The global variable is left completely alone, so after the sub program ends it still holds its original value, even though the sub program appeared to change it.
bottles = 24
def count_crate():
bottles = 30
print("In the crate:", bottles)
count_crate()
print("In the cupboard:", bottles)Output:
In the crate: 30
In the cupboard: 24
If the sub program starts with global followed by the name, there is only one variable: the shared global. Assigning to the name inside the sub program changes that single variable, so the new value is still there after the sub program ends and the main program prints it.
bottles = 24
def count_crate():
global bottles
bottles = 30
print("In the crate:", bottles)
count_crate()
print("In the cupboard:", bottles)Output:
In the crate: 30
In the cupboard: 30
To work out what a program with a name clash prints, trace each use of the name separately. First ask whether a global statement for that name appears at the start of the sub program. If it does, every use inside is the global. If it does not, an assignment inside the sub program makes the name local throughout that sub program, so every use inside is the local. Then follow the values line by line, and for each print statement write down the value and the reason: local or global.
A local variable earns its place because it makes a sub program easier to write, test, reuse and maintain, not just because it keeps a value tidy.
A sub program that uses only its own local variables and its parameters is self-contained: the values it needs from the rest of the program arrive as parameters, so it depends on nothing else. That means you can test it on its own, before the rest of the program is written or working, by simply calling it with some values and checking what comes back. It also means you can copy it into another program and call it there without changing a line, because it carries all its working inside itself.
def lap_time(seconds):
# minutes and extra are local to this function
minutes = seconds // 60
extra = seconds % 60
return str(minutes) + "m " + str(extra) + "s"
print(lap_time(95))
print(lap_time(200))Output:
1m 35s
3m 20s
Each call passes in a different number of seconds, yet the function needs no other part of the program to do its job. minutes and extra belong to that one function, so a change to a variable called minutes somewhere else in the program could not disturb them.
Because no other part of the program can reach a local variable, nothing outside the sub program can alter it behind your back. If a result comes out wrong, the fault must lie inside that single sub program, so you search a few lines instead of the whole program, and the bug is found faster.
Local variables also let the same name appear in several sub programs without clashing. Each sub program can use the obvious name for its own job, which makes the code easier to read and to maintain later. Finally, the memory a local variable uses is released when the sub program ends, so a program that runs for a long time does not fill memory with values that are already finished with.
A global variable is created in the main program, outside every sub program, so any part of the program can use it. It is the sensible choice when a value really is needed by lots of the program: if several sub programs must know the same setting, or all work on the same running total, going global saves you passing it in over and over, and any sub program can read it without being handed anything. The code stays short and easy to follow.
club_name = "Riverside Runners" # one setting the whole program follows
def welcome(name):
print("Welcome to " + club_name + ", " + name)
def cheer(name):
print(club_name + " cheers for " + name)
welcome("Ada")
cheer("Ada")Output:
Welcome to Riverside Runners, Ada
Riverside Runners cheers for Ada
Because a global belongs to the whole program, any part of the program can change it. If it ends up holding the wrong value, there is no easy way to tell which sub program put it there, so the program is harder to debug. It is also harder to maintain, because an edit inside one sub program can affect code written somewhere else entirely. A global lasts from the moment it is created until the program ends, so it occupies memory for the whole run, even while nothing is using it at that moment. Globals are not always a mistake, though — the problem is having many of them.
Pass values into a sub program as parameters and send results back out with return values. The sub program then depends on nothing outside itself, so you can test it in isolation and drop it into a different program without changing a line. Keep globals for the few values genuinely needed everywhere, and write any fixed value as a constant with a name in capitals, such as POSTAGE = 2.75, so the capitals warn everyone that it must not be reassigned.
def lap_total(lap1, lap2):
return lap1 + lap2
morning = lap_total(48, 51)
evening = lap_total(47, 52)
print("Total seconds: " + str(morning + evening))Output:
Total seconds: 198A constant is a named value that is set once and never changed. Constants have scope in exactly the same way as variables: put one at the top level of your program, outside every sub program, and it is global; put one inside a sub program and it is local.
A global constant is declared at the top level of your program, outside every sub program, and its name is written in capital letters. Every sub program can read it, so the value is stored in one place only: if the value ever needs to change, you edit one line instead of hunting through the program for every copy.
No global statement is needed to read a global constant inside a sub program. A global statement is only needed when a sub program assigns to a global, and a constant is never assigned to after it is set, so reading works straight away.
PERIOD_MINUTES = 50 # global constant, read by every sub program
def minutes_since_start(periods):
return periods * PERIOD_MINUTES
def minutes_left(periods_done, total_periods):
return (total_periods - periods_done) * PERIOD_MINUTES
print(minutes_since_start(3))
print(minutes_left(2, 6))Output:
150
200
Both sub programs read the same PERIOD_MINUTES, so the length of a period is defined in exactly one place.
A local constant belongs to one sub program and nowhere else. You declare it on a line inside that sub program because the fixed value it holds matters only to that routine. Nothing outside can read it, and it disappears when the sub program ends, so the main program cannot use it at all.
def book_slot(people):
MAX_PER_SLOT = 8 # local constant, only this sub program needs it
if people <= MAX_PER_SLOT:
return "Slot booked"
return "Too many people for one slot"
print(book_slot(4))
print(book_slot(9))Output:
Slot booked
Too many people for one slot
Python does not enforce constants: writing a name in capitals does not stop a later line assigning a new value to it, so keeping a constant constant is the programmer's job.