Leer waar variabelenscopes om draaien en raak vertrouwd met de 'LEGB'-regel. Je ziet ook scenario’s waarin de keywords global en nonlocal in actie komen.
Meteen na een paar regels code "name is not defined" of "local variable referenced before assignment" zien is frustrerend. De code oogt prima, maar Python is het er niet mee eens. De oorzaak is bijna altijd scope: waar een naam zichtbaar is en naar welk object hij op dat moment verwijst.
Zodra je de naamresolutie van Python begrijpt—vaak samengevat als de LEGB-regel—worden deze fouten voorspelbaar en eenvoudig te verhelpen. In deze tutorial behandel ik stap voor stap variabelen en scopes in Python.
Wat is een variabele?
Een variabele is een naam die aan een object is gebonden. In Python maak je zo'n binding met de toewijzingsoperator =. Namen hebben geen type; ze kunnen naar elk object verwijzen (string, int, list, function, enz.).
customer_name ="Ava Torres"
order_count =1
total_cents =1+2*300# evaluated first, then boundMogelijk gemaakt door
Volg deze regels en conventies bij het benoemen van variabelen om syntaxfouten en subtiele bugs te voorkomen.
Gebruik alleen letters, cijfers en underscores; namen mogen niet met een cijfer beginnen.
Gebruik geen keywords zoals True, for of class als naam.
Vermijd het overschaduwen van built-ins zoals list, dict, sum of max.
Geef de voorkeur aan snake_case voor leesbaarheid (PEP 8).
Deze voorbeelden laten zien wat je niet moet doen en welke fouten Python geeft:
first string value ="First string"# spaces not allowed# SyntaxError: invalid syntax
1st_value =10# cannot start with a digit# SyntaxError: invalid decimal literalTrue="yes"# cannot assign to a keyword# SyntaxError: cannot assign to TrueMogelijk gemaakt door
Hoe Python-scope werkt (LEGB)
Scope is het codegebied waar een naam zichtbaar is. Wanneer je een naam gebruikt, zoekt Python deze in deze volgorde op (LEGB):
Global: het topniveau van de module (modulenamespace).
Built-in: namen die door Python in builtins zijn gedefinieerd (bijvoorbeeld len, print).
Namen leven in namespaces (denk aan dictionaries die namen aan objecten koppelen). Scope gaat over welke namespaces Python raadpleegt om een naam op een bepaald punt in de code te resolven.
Lokale scope
Namen die binnen een functie worden toegewezen, zijn lokaal voor die functie, tenzij anders gedeclareerd. Ze zijn niet zichtbaar buiten de functie.
defshow_order_id():
order_id =42print("inside function:", order_id)
show_order_id()print("outside function:", order_id)# NameErrorMogelijk gemaakt door
Python bepaalt scope tijdens het compileren. Als een functie ergens in haar body aan een naam toewijst, worden alle gebruiken van die naam in de functie als lokaal beschouwd—wat leidt tot een veelvoorkomende UnboundLocalError wanneer je leest voordat je toewijst:
discount_rate =0.10# module-level (global)defprice_with_discount(amount_cents):print("configured discount:", discount_rate)# looks local because of the assignment below
discount_rate =0.20# assignment makes 'discount_rate' local in this functionreturnint(amount_cents *(1- discount_rate))# UnboundLocalError: cannot access local variable 'discount_rate' where it is not associated with a valueMogelijk gemaakt door
Om de module-niveau naam in de functie te gebruiken, vermijd je óf de toewijzing, óf markeer je hem als global (hieronder behandeld).
Enclosing scope (closures)
Geneste functies kunnen namen zien uit hun direct omvattende functie. Om zo'n naam opnieuw te binden (niet alleen lezen), declareer je hem als nonlocal.
defmake_step_counter():
count =0# enclosing scope for 'increment'defincrement():nonlocal count # rebind the 'count' in the nearest enclosing function
count +=1return count
return increment
step = make_step_counter()print(step())# 1print(step())# 2Mogelijk gemaakt door
Zonder nonlocal zou toewijzen aan count binnen increment() een nieuwe lokale naam creëren en de buitenste count ongemoeid laten.
Globale scope
Namen die op het topniveau van een module worden toegewezen, leven in de globale namespace van de module. Elke functie kan ze lezen. Om binnen een functie aan een naam op module-niveau toe te wijzen, declareer je hem als global.
greeting ="Hello"defgreet_city(city_name):print(greeting, city_name)# reads globaldefset_greeting(new_greeting):global greeting
greeting = new_greeting # rebinds global
greet_city("Nairobi")# Hello Nairobi
set_greeting("Hi")
greet_city("Nairobi")# Hi NairobiMogelijk gemaakt door
Gebruik global spaarzaam. Geef de voorkeur aan waarden doorgeven als parameters en resultaten retourneren om code testbaar en voorspelbaar te houden.
Built-in scope (en een opmerking over keywords)
De built-in scope bevat namen zoals len, print en Exception. Vermijd het overschaduwen ervan, anders verlies je in die scope de toegang tot de built-in.
list=[1,2,3]# shadows the built-in 'list' constructorlist("abc")# TypeError: 'list' object is not callabledellist# fix by deleting the shadowing nameMogelijk gemaakt door
Keywords (zoals if, for, def) maken deel uit van de Python-syntaxis, niet van de built-in namespace, en kunnen nooit als identifiers worden gebruikt.
Blokken, lussen en comprehensions
De blokstatements en comprehensions van Python hebben specifieke scope-eigenschappen die ontwikkelaars uit andere talen vaak verrassen.
Er is geen block scope voor if/for/while/with
Toewijzingen binnen deze blokken beïnvloeden de omvattende scope (functie of module). Lusvariabelen blijven ook gedefinieerd nadat de lus is afgelopen.
ifTrue:
status ="ready"print(status)# "ready"for i inrange(3):passprint(i)# 2 (the last value from the loop)Mogelijk gemaakt door
Comprehensions isoleren hun iteratievariabele
List-, dict- en set-comprehensions hebben hun eigen lokale scope voor lusvariabelen. De iteratievariabele lekt niet naar de omringende scope.
numbers =[1,2,3][x for x in numbers]print("x"inglobals()or"x"inlocals())# FalseMogelijk gemaakt door
Python 3.12 (PEP 709) inline-de comprehensions voor snelheid, met behoud van deze isolatie; je krijgt nog steeds duidelijke, niet-lekkende lusvariabelen met snellere uitvoering.
Het keyword global gebruiken
Declareer een naam als global binnen een functie wanneer je een variabele op module-niveau opnieuw moet binden. Plaats de declaratie bovenaan de functie zodat duidelijk is welke variabele je wijzigt.
tax_rate =0.08defconfigure_tax(rate):global tax_rate
tax_rate =float(rate)deftotal_with_tax(cents):returnint(cents *(1+ tax_rate))
configure_tax(0.10)print(total_with_tax(1000))# 1100Mogelijk gemaakt door
Het keyword nonlocalgebruiken
Gebruik nonlocal om een naam uit de dichtstbijzijnde omvattende functiescope opnieuw te binden. Dit is gebruikelijk in closures die state bijhouden.
defmake_accumulator(start=0):
total = start
defadd(amount):nonlocal total
total += amount
return total
return add
acc = make_accumulator()print(acc(5))# 5print(acc(10))# 15Mogelijk gemaakt door
Het gebruik van nonlocal voor een naam die niet in een omvattende functie is gedefinieerd, is een SyntaxError. Als de naam global is, gebruik dan global.
locals() en globals() in 2025
Deze functies zijn handig voor inspectie en debuggen, niet om variabelen bij te werken. Vanaf Python 3.13 (PEP 667) geeft elke aanroep van locals() in een functie een onafhankelijke snapshot terug. Het bewerken van die snapshot verandert de echte locals niet. Ik heb dit bevestigd door het volgende fragment op Python 3.13 uit te voeren:
defprobe():
project ="alpha"
snap1 =locals()
snap1["project"]="beta"# edits the snapshot only
observed = project # still "alpha"
snap2 =locals()# new snapshotreturn snap1["project"], observed, snap2["project"]print(probe())# ('beta', 'alpha', 'alpha')Mogelijk gemaakt door
Gebruik globals() op een vergelijkbare manier voor de modulenamespace. Geef bij applicatiecode de voorkeur aan expliciete parameters en returnwaarden boven dynamische lookups.
Veelvoorkomende valkuilen en hoe je ze oplost
Deze fouten verklaren het merendeel van de scope-gerelateerde issues die ik in de praktijk zie.
UnboundLocalError door toewijzing in een functie: Python beschouwt een naam als lokaal als eraan ergens in de functie wordt toegewezen. Verplaats het lezen na de toewijzing, hernoem, of voeg waar passend global/nonlocal toe.
Blokscope verwachten: Namen die in if/for/while/with worden toegewezen, blijven bestaan in de omvattende scope. Gebruik kleinere helperfuncties om de scope te beperken.
Built-ins overschaduwen: Vermijd namen als list, dict, sum, id of min. Kies beschrijvende namen zoals customer_list of min_allowed.
nonlocal vergeten in closures: Als je een variabele uit een buitenstaande functie wilt bijwerken, declareer hem dan als nonlocal. Anders maak je een nieuwe lokale aan en verandert de buitenste waarde niet.
Keywords verwarren met built-ins: Keywords (syntaxis) kunnen nooit als namen worden gebruikt. Built-ins kunnen worden overschaduwd maar dat is onwenselijk.
Best practices om scope eenvoudig te houden
Deze gewoontes maken code makkelijker leesbaar, testbaar en te debuggen.
Geef de voorkeur aan kleine, gefocuste functies. Definieer variabelen in de kleinst mogelijke scope die werkt.
Geef data door en terug via parameters en returnwaarden. Minimaliseer state op module-niveau.
Gebruik closures bewust. Documenteer welke buitenliggende naamen een geneste functie vastlegt, en gebruik nonlocal wanneer je echt opnieuw moet binden.
Kies beschrijvende, niet-conflicterende namen. Een leading underscore (bijvoorbeeld _cache) duidt intern gebruik aan.
Beschouw locals()/globals() als read-only diagnostiek, niet als configuratiemechanisme.
Conclusie
Python resolveert namen door te zoeken in Local → Enclosing → Global → Built-in scopes. Het begrijpen van die volgorde—en wanneer je global of nonlocal gebruikt—elimineeert veelvoorkomende fouten zoals NameError en UnboundLocalError. Houd variabelen in de kleinst haalbare scope, vermijd het overschaduwen van built-ins en gebruik closures doelbewust. Met dat mentale model gedragen je functies zich voorspelbaar en houdt scope op een bron van verrassingen te zijn.
Ik heb in verschillende sectoren gewerkt en meerdere petten gedragen: softwareontwikkelaar, machine learning-onderzoeker, data scientist, productmanager. Maar in de kern ben ik een programmeur die graag leert en kennis deelt!
Duik in datawetenschap met Python en leer hoe je je data goed kunt analyseren en visualiseren. Je hoeft niet te kunnen programmeren of iets dergelijks.
Meteen na een paar regels code "name is not defined" of "local variable referenced before assignment" zien is frustrerend. De code oogt prima, maar Python is het er niet mee eens. De oorzaak is bijna altijd scope: waar een naam zichtbaar is en naar welk object hij op dat moment verwijst.
Zodra je de naamresolutie van Python begrijpt—vaak samengevat als de LEGB-regel—worden deze fouten voorspelbaar en eenvoudig te verhelpen. In deze tutorial behandel ik stap voor stap variabelen en scopes in Python.
Wat is een variabele?
Een variabele is een naam die aan een object is gebonden. In Python maak je zo'n binding met de toewijzingsoperator
=. Namen hebben geen type; ze kunnen naar elk object verwijzen (string, int, list, function, enz.).Volg deze regels en conventies bij het benoemen van variabelen om syntaxfouten en subtiele bugs te voorkomen.
True,forofclassals naam.list,dict,sumofmax.snake_casevoor leesbaarheid (PEP 8).Deze voorbeelden laten zien wat je niet moet doen en welke fouten Python geeft:
Hoe Python-scope werkt (
LEGB)Scope is het codegebied waar een naam zichtbaar is. Wanneer je een naam gebruikt, zoekt Python deze in deze volgorde op (LEGB):
builtinszijn gedefinieerd (bijvoorbeeldlen,print).Namen leven in namespaces (denk aan dictionaries die namen aan objecten koppelen). Scope gaat over welke namespaces Python raadpleegt om een naam op een bepaald punt in de code te resolven.
Lokale scope
Namen die binnen een functie worden toegewezen, zijn lokaal voor die functie, tenzij anders gedeclareerd. Ze zijn niet zichtbaar buiten de functie.
Python bepaalt scope tijdens het compileren. Als een functie ergens in haar body aan een naam toewijst, worden alle gebruiken van die naam in de functie als lokaal beschouwd—wat leidt tot een veelvoorkomende
UnboundLocalErrorwanneer je leest voordat je toewijst:Om de module-niveau naam in de functie te gebruiken, vermijd je óf de toewijzing, óf markeer je hem als
global(hieronder behandeld).Enclosing scope (closures)
Geneste functies kunnen namen zien uit hun direct omvattende functie. Om zo'n naam opnieuw te binden (niet alleen lezen), declareer je hem als
nonlocal.Zonder
nonlocalzou toewijzen aancountbinnenincrement()een nieuwe lokale naam creëren en de buitenstecountongemoeid laten.Globale scope
Namen die op het topniveau van een module worden toegewezen, leven in de globale namespace van de module. Elke functie kan ze lezen. Om binnen een functie aan een naam op module-niveau toe te wijzen, declareer je hem als
global.Gebruik
globalspaarzaam. Geef de voorkeur aan waarden doorgeven als parameters en resultaten retourneren om code testbaar en voorspelbaar te houden.Built-in scope (en een opmerking over keywords)
De built-in scope bevat namen zoals
len,printenException. Vermijd het overschaduwen ervan, anders verlies je in die scope de toegang tot de built-in.Keywords (zoals
if,for,def) maken deel uit van de Python-syntaxis, niet van de built-in namespace, en kunnen nooit als identifiers worden gebruikt.Blokken, lussen en comprehensions
De blokstatements en comprehensions van Python hebben specifieke scope-eigenschappen die ontwikkelaars uit andere talen vaak verrassen.
Er is geen block scope voor
if/for/while/withToewijzingen binnen deze blokken beïnvloeden de omvattende scope (functie of module). Lusvariabelen blijven ook gedefinieerd nadat de lus is afgelopen.
Comprehensions isoleren hun iteratievariabele
List-, dict- en set-comprehensions hebben hun eigen lokale scope voor lusvariabelen. De iteratievariabele lekt niet naar de omringende scope.
Python 3.12 (PEP 709) inline-de comprehensions voor snelheid, met behoud van deze isolatie; je krijgt nog steeds duidelijke, niet-lekkende lusvariabelen met snellere uitvoering.
Het keyword
globalgebruikenDeclareer een naam als
globalbinnen een functie wanneer je een variabele op module-niveau opnieuw moet binden. Plaats de declaratie bovenaan de functie zodat duidelijk is welke variabele je wijzigt.Het keyword
nonlocalgebruikenGebruik
nonlocalom een naam uit de dichtstbijzijnde omvattende functiescope opnieuw te binden. Dit is gebruikelijk in closures die state bijhouden.Het gebruik van
nonlocalvoor een naam die niet in een omvattende functie is gedefinieerd, is eenSyntaxError. Als de naam global is, gebruik danglobal.locals()englobals()in 2025Deze functies zijn handig voor inspectie en debuggen, niet om variabelen bij te werken. Vanaf Python 3.13 (PEP 667) geeft elke aanroep van locals() in een functie een onafhankelijke snapshot terug. Het bewerken van die snapshot verandert de echte locals niet. Ik heb dit bevestigd door het volgende fragment op Python 3.13 uit te voeren:
Gebruik
globals()op een vergelijkbare manier voor de modulenamespace. Geef bij applicatiecode de voorkeur aan expliciete parameters en returnwaarden boven dynamische lookups.Veelvoorkomende valkuilen en hoe je ze oplost
Deze fouten verklaren het merendeel van de scope-gerelateerde issues die ik in de praktijk zie.
UnboundLocalErrordoor toewijzing in een functie: Python beschouwt een naam als lokaal als eraan ergens in de functie wordt toegewezen. Verplaats het lezen na de toewijzing, hernoem, of voeg waar passend global/nonlocal toe.customer_listofmin_allowed.nonlocalvergeten in closures: Als je een variabele uit een buitenstaande functie wilt bijwerken, declareer hem dan als nonlocal. Anders maak je een nieuwe lokale aan en verandert de buitenste waarde niet.Best practices om scope eenvoudig te houden
Deze gewoontes maken code makkelijker leesbaar, testbaar en te debuggen.
_cache) duidt intern gebruik aan.Conclusie
Python resolveert namen door te zoeken in Local → Enclosing → Global → Built-in scopes. Het begrijpen van die volgorde—en wanneer je global of nonlocal gebruikt—elimineeert veelvoorkomende fouten zoals
NameErrorenUnboundLocalError. Houd variabelen in de kleinst haalbare scope, vermijd het overschaduwen van built-ins en gebruik closures doelbewust. Met dat mentale model gedragen je functies zich voorspelbaar en houdt scope op een bron van verrassingen te zijn.