9c Scope
Een functie mag gebruiken wat erbuiten staat:
naam = "Sam"
def groet():
print(f"Hallo {naam}")
groet()
Uitvoer: Hallo Sam. De variabele naam staat buiten de functie, en groet() komt er gewoon bij.
Veranderen is een ander verhaal.
Voorspel: wat doet dit?
teller = 0
def tel_op():
teller = teller + 1
tel_op()
print(teller)
Klik hier voor het antwoord.
Het programma stopt met een foutmelding:
UnboundLocalError: cannot access local variable 'teller' where it is not associated with a value
Werk je met Python 3.10 of ouder, dan staat er local variable 'teller' referenced before assignment. Dezelfde fout, andere bewoording.
Waarom dat misgaat
Zodra ergens in een functie teller = ... staat, besluit Python: teller is hier een lokale variabele, eentje die alleen binnen deze functie bestaat. Dat geldt voor de hele functie, ook voor de regels ervóór.
Op teller = teller + 1 moet hij eerst de rechterkant uitrekenen. Daarvoor heeft hij die lokale teller nodig, en die heeft nog geen waarde gekregen. Vandaar de fout.
De plek waar een variabele bestaat heet zijn scope. Wat je in een functie maakt, bestaat alleen daar:
def maak():
binnenin = 42
maak()
print(binnenin)
Dit geeft NameError: name 'binnenin' is not defined. Buiten de functie bestaat binnenin niet.
Met global mag het wel
Met global zeg je: ik bedoel de variabele van buiten, geen nieuwe.
teller = 0
def tel_op():
global teller
teller = teller + 1
tel_op()
tel_op()
print(teller)
Uitvoer: 2. De regel global teller staat bovenaan de functie, vóór het eerste gebruik.
Meestal wil je return
Met global kan elke functie in je programma dezelfde variabele aanpassen. Bij een lang programma wordt dan lastig terug te vinden wie hem wanneer veranderde. Met return uit 9b blijft dat zichtbaar:
teller = 0
def opgeteld(getal):
return getal + 1
teller = opgeteld(teller)
teller = opgeteld(teller)
print(teller)
Uitvoer: 2, net als hierboven. De functie rekent, en buiten de functie bepaal jij wat er met het antwoord gebeurt.
Vuistregel: kan het met return, doe het met return. global heb je nodig als jij de functie niet zelf aanroept en er dus niets aan kunt teruggeven — bijvoorbeeld een functie die afgaat zodra iemand op een toets drukt. Dat kom je tegen in 4.5 Een score bijhouden van de play-cursus.
Er gaat iets mis
UnboundLocalError: cannot access local variable 'score' where it is not associated with a value
Oorzaak: een functie verandert een variabele van buiten, zonder global.
Oplossing: zet global bovenaan de functie, of laat de functie de nieuwe waarde teruggeven met return.
# FOUT
score = 0
def punt():
score = score + 1
punt()
# GOED
score = 0
def punt():
global score
score = score + 1
punt()
Opdracht 9c.1: Repareer de teller
Dit programma zou 3 moeten printen, maar geeft een UnboundLocalError. Repareer het met global.
teller = 0
def tel_op():
teller = teller + 1
tel_op()
tel_op()
tel_op()
print(teller)Klik hier voor een tip.
Er hoeft maar één regel bij, als eerste regel binnen de functie.
Klik hier voor de oplossing.
teller = 0
def tel_op():
global teller
teller = teller + 1
tel_op()
tel_op()
tel_op()
print(teller)
Uitvoer: 3.
teller = 0
def tel_op():
global teller
teller = teller + 1
tel_op()
tel_op()
tel_op()
print(teller)Opdracht 9c.2: Dezelfde teller zonder global
Los opdracht 9c.1 nog een keer op, nu zonder global. Laat tel_op het opgehoogde getal teruggeven en vang het buiten de functie op.
Verwachte uitvoer:
3
# Schrijf hier je code
Klik hier voor een tip.
Geef de functie een parameter, zodat je hem het huidige getal kunt meegeven: def tel_op(getal):. Buiten de functie schrijf je dan teller = tel_op(teller).
Klik hier voor de oplossing.
teller = 0
def tel_op(getal):
return getal + 1
teller = tel_op(teller)
teller = tel_op(teller)
teller = tel_op(teller)
print(teller)
Deze versie heeft geen global nodig: de functie rekent alleen, en de teller wordt buiten de functie bijgewerkt.
teller = 0
def tel_op(getal):
return getal + 1
teller = tel_op(teller)
teller = tel_op(teller)
teller = tel_op(teller)
print(teller)Opdracht 9c.3: Twee keer dezelfde naam (uitdaging)
Voorspel wat dit programma print. Draai het daarna en kijk of je gelijk had.
naam = "Sam"
def begroet():
naam = "Alex"
print(naam)
begroet()
print(naam)Klik hier voor een tip.
Er zijn hier twee variabelen die allebei naam heten. Bedenk per print welke van de twee aan de beurt is.
Klik hier voor de oplossing.
Alex
Sam
Binnen begroet maakt de regel naam = "Alex" een nieuwe, lokale naam. Die verdwijnt zodra de functie klaar is, dus de naam van buiten staat daarna nog gewoon op "Sam".
En anders dan bij de teller hierboven krijg je hier géén UnboundLocalError: de functie leest de lokale naam pas nadat hij een waarde heeft gekregen.
Wilde je de buitenste naam echt veranderen, dan had er global naam bovenaan de functie moeten staan.