Local and global variables

Scope: where a variable exists, and why passing values beats global.

F4.3Functions and structured codeGCSE15 min

Covers OCR J277 2.2.3 Additional programming techniques · AQA 8525 3.2.10 Structured programming and subroutines (procedures and functions) · Edexcel 1CP2 6.6.3 Local and global variables

Do this lesson in the simulator

A variable made inside a function is not the same as one made outside it, even when they have the same name. Where a variable can be used is called its scope. Getting scope wrong gives some of the most confusing errors in Python; getting it right is what lets functions be written and tested one at a time.

Local variables stay inside

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

def measure():
    gap = distance()
    print("inside, gap is", gap)

measure()
print(gap)

Run this in the simulator

NameError on the last line. gap is a local variable: it is created when measure runs and it disappears when measure returns. The main program never had a gap. If the outside needs the value, the function should return it.

Along a timeline: gap is made when measure runs and is gone when measure returns, so print(gap) afterwards finds nothingmain programmeasure() runninggap existsmeasure() is calledgap = distance()measure returns:gap is goneprint(gap)no gap hereNameErrortime
A local variable lives only while its function runs. gap is made by the line inside measure and thrown away when measure returns, so by the time the main program asks for it there is nothing to find.

Same name, different variables

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

speed = 80

def gentle_drive():
    speed = 30
    print("inside the function, speed is", speed)
    forward(speed, distance=15)

gentle_drive()
print("outside, speed is still", speed)

Run this in the simulator

The speed = 30 inside the function made a new local variable that happened to share the name. The speed outside, a global variable because it was made in the main program, was never touched. This is a feature, not a bug: whoever writes gentle_drive does not need to know every variable name in the rest of the program.

Two boxes: the global speed = 80 in the main program, and a separate local speed = 30 inside gentle_drivemain program: globalspeed = 80gentle_drive(): localspeed = 30print(..., speed)shows 30print(..., speed)shows 80made in themain programmade inside the function,gone when it ends
Two variables that happen to share a name. Each print reads the speed in its own box: 30 inside the function, 80 outside. Setting the inner one never touches the outer one.

Reading a global works; changing it does not

A function can read a global variable, but assigning to that name inside a function makes a local one instead. Run this:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

beeps = 0

def beep():
    tone(880, 0.1)
    beeps = beeps + 1

beep()
print("beeps:", beeps)

Run this in the simulator

UnboundLocalError. Because the function assigns to beeps, Python treats beeps as local everywhere in that function, so beeps + 1 tries to read a local variable that has no value yet. There are two ways to fix it.

Fix 1: pass it in and return it

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

def beep(count):
    tone(880, 0.1)
    wait(0.1)
    return count + 1

beeps = 0
beeps = beep(beeps)
beeps = beep(beeps)
print("beeps:", beeps)

Run this in the simulator

The function takes the count as a parameter and returns the new count. Everything it uses comes in through its parameters, and everything it produces goes out through return, so it can be understood and tested on its own. This is usually the better fix.

Fix 2: the global keyword

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

beeps = 0

def beep():
    global beeps
    tone(880, 0.1)
    wait(0.1)
    beeps = beeps + 1

beep()
beep()
print("beeps:", beeps)

Run this in the simulator

global beeps tells Python that beeps in this function means the global one. It works, but now beep quietly changes something outside itself: a reader of the main program cannot see that beep() changes beeps, and any function anywhere could be the one that changed it. Use global sparingly, for a few values the whole program genuinely shares.

Fix 1 passes beeps in as count and returns the new count; fix 2 uses global to change beeps from inside the functionFix 1: in and outmain programbeepsbeeps = beep(beeps)def beep(count):return count + 1...inoutboth moves show in the callFix 2: globalmain programbeepsbeep()def beep():global beeps...the call does not show it
Both fixes end with beeps at 2. In fix 1 the count goes in and the new count comes out, both visible in the call. In fix 2 the function reaches out and changes beeps itself, and nothing in beep() tells the reader.

Task: count the beeps

This program should beep three times and print beeps: 3, but it crashes. Fix it, with parameters and return or with global.

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

beeps = 0

def beep():
    tone(880, 0.1)
    wait(0.1)
    beeps = beeps + 1

beep()
beep()
beep()
print("beeps:", beeps)

Challenges

  1. Write longest_so_far(reading, best) that returns whichever is larger, and use it in a loop of readings without any global variables.
  2. Add a global constant NOTE = 660 and use it inside beep. Why does reading it work without global?
  3. Find a program from an earlier lesson that uses variables inside a function, and say which are local and which are global.

Past paper questions on this

Real exam questions that test this, each worked through with a program you can run.