3.5. Scope#

Met scope bedoelen we welke variabelen door welk deel van de code aangesproken kunnen worden. Tot nu toe hebben we hier nog niet mee te maken gehad om dat alle code dezelfde scope had, namelijk de scope van het gehele script (ook wel ‘global’ genoemd). Nu we functies gaan gebruiken moeten we hier echter wel rekening mee houden.

x = 'global x'
print(x)
global x

Van de bovenstaande code kan je nu waarschijnlijk al wel goed voorspellen wat het doet. We definiëren een globale variabele x en geven deze de waarde 'global x'. In de regel daaronder printen we deze waarden, en we krijgen de tekst global x in onze shell te zien. Alle code in dit voorbeeld bevindt zich in de global scope. We hebben immers geen functies gebruikt in dit stukje code.

Laten we nu naar het volgende voorbeeld kijken, wat denk jij dat de onderstaande code zal doen?

x = 'global x'

def test():
    x = 'local x'
    print(x)

test()
print(x)
local x
global x

In deze code wordt er twee keer de variabele x gedefinieerd, één keer in de globale scope, waar het de waarde 'global x' meekrijgt, en één keer in de locale scope van de functie test, waar het de waarde 'local x' meekrijgt.

Binnen de functie vragen we aan python om de waarde van de variabele x te printen. Python gaat dan eerst in de lokale scope (dus binnen de functie) op zoek naar een variabele met die naam. Alleen wanneer deze lokaal niet gevonden kan worden zal Python in de globale scope verder zoeken. In dit geval bestaat de variabele x in de lokale scope en dus wordt de bijbehorende waarde local x geprint door de functie test().

Hierna vragen we Python ook nog buiten de functie om de waarde van de variabele x te printen. Omdat we dit nu op het globale niveau van de code vragen zoekt Python alleen in de globale scope naar de variabele x. In deze scope heeft x de waarde global x, dus dat wordt nu geprint. Merk ook op dat de regel x = local x in de functie de waarde van de globale variabele niet aangepast heeft, in plaats daarvan werd er een nieuwe lokale variabele aangemaakt.

Laten we nu eens naar het onderstaande voorbeeld kijken. Kijk eerst eens voor jezelf of je begrijpt wat hier gebeurt.

x = 'global x'

def test():
    y = 'local y'
    print(x)
    print(y)

test()
print(x)
global x
local y
global x

In dit geval is x een globale variabele en is y een locale variabele binnen de functie test(). Wanneer we deze functie uitvoeren zal deze variabele de waarde 'local y' krijgen. Daarna vragen we Python om de waarde van de variabele x te printen. Deze bestaat niet in de locale scope, dus zoekt Python deze op in de globale scope. Daar vindt het deze en daarom wordt er global x geprint. Daarna vragen we om de waarde van y te printen, die bestaat lokaal en dus wordt er ook local y geprint. Ten slotte printen we de globale variabele x nog.

Je ziet hier dus dat je vanuit een functie wel de globale variabelen kan opvragen. Wat zou er gebeuren wanneer we nu onderaan deze code een regel toevoegen met daarin print(y)?

In dat geval zal je een foutmelding krijgen. Op het globale niveau van de functie zijn de lokale variabelen niet beschikbaar. Omdat er geen variabele met de naam y is in de globale scope zal er geen variabele gevonden worden en krijg je een NameError.

Laten we nu naar een laatste voorbeeld kijken.

x = 'global x'
y = 'global y'

def test(x):
    y = 'local y'
    print(x)
    print(y)

test(y)
global y
local y

We hebben nu de functie test gedefinieerd zodat deze een input argument x heeft. Een input argument wordt gezien als een lokale variabele. Bij het uitvoeren van de functie wordt dus direct een lokale variabele aangemaakt met de naam x en de waarde die opgegeven wordt bij het aanroepen van de functie.

In dit geval hebben wij twee globale variabelen: x en y en roepen we de functie aan met als argument de globale variabele y. Zodra we deze functie aanroepen wordt er een lokale variabele x aangemaakt met de waarde van deze globale y: global y. Wanneer we vervolgens binnen de functie vragen om de waarde van x te printen wordt hier dus ook de waarde van de lokale variabele x voor gebruikt: global y.

Je merkt waarschijnlijk al wel dat het heel verwarrend kan worden wanneer variabelen in de lokale scope dezelfde namen hebben als die in de globale scope. Daarom raden wij je aan om dit te vermijden. Geef elke nieuwe variabele die je aanmaakt altijd een nieuwe, unieke naam die zo goed mogelijk beschrijft welke informatie je in deze variabele op zal slaan.

Daarnaast is het een goed idee om binnen functies vrijwel alleen met lokale variabelen te werken. Zorg ervoor dat alle informatie die de functie nodig heeft via de input argumenten binnen komt en via het return statement weer teruggegeven wordt. Deze aanpak noemen we ook wel “Functional Programming” en zorgt over het algemeen voor code waar fouten makkelijker op te sporen zijn. Kijk eens naar de volgende twee voorbeelden, welke vind jij duidelijker?

a = 5
b = 10

def a_plus_b():
    c = a + b
    return c

def a_min_c():
    d = a - c
    return d

c = a_plus_b()
b = a_min_c()
a = a_plus_b()

print(a, b, c)
-5 -10 15
def optellen(a, b):
    c = a + b
    return c

def aftrekken(a, b):
    c = a - b
    return c

a = 5
b = 10

c = optellen(a, b)
d = aftrekken(a, c)
e = optellen(a, b)

print(a, b, c, d, e)
5 10 15 -10 15

Beide stukken code doen hetzelfde, maar de tweede is duidelijker en flexibeler. De functies in het eerste stuk code zijn alleen toepasbaar binnen dat stukje code. De functies uit het tweede stukje zijn veel breder toepasbaar dan alleen hier, je zou ze zo naar een ander stuk code kunnen kopiëren.