Local and global variables
Scope: where a variable exists, and why passing values beats global.
Do this lesson in the simulatorA 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)
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.
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)
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.
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)
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)
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)
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.
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
- Write
longest_so_far(reading, best)that returns whichever is larger, and use it in a loop of readings without any global variables. - Add a global constant
NOTE = 660and use it insidebeep. Why does reading it work withoutglobal? - Find a program from an earlier lesson that uses variables inside a function, and say which are local and which are global.