Variablers räckvidd (Scope)

Förstå lokala och globala variabler samt undvika räckviddsrelaterade fel.

Ca 45 min · del av Programmering i Python

Begrepp i lektionen

  • scope
  • lokal variabel
  • global variabel
  • LEGB-regeln

Efter denna lektion kommer du att förstå hur variablers räckvidd (scope) fungerar i Python, hur LEGB-regeln tillämpas samt hur du undviker vanliga räckviddsrelaterade fel i dina program.

Vad är räckvidd (Scope)?

I programmering syftar räckvidd (*scope*) på den del av koden där en viss variabel är synlig och tillgänglig. Variabler som skapas i ett program finns inte automatiskt överallt, utan deras livslängd och synlighet beror på var de definieras.

Python har i huvudsak två grundläggande typer av räckvidd för variabler:

1. Lokal räckvidd (Local scope): En variabel som skapas inuti en funktion tillhör funktionens lokala räckvidd. Den tänder sin livsgnist när funktionen anropas och förstörs när funktionen avslutas. Den kan inte nås från kod utanför funktionen.

2. Global räckvidd (Global scope): En variabel som definieras på huvudnivån i en fil (utanför alla funktioner) har global räckvidd. Den är tillgänglig från det ögonblick den skapas ända tills programmet avslutas, och kan läsas inuti funktioner.

Se följande exempel:

python

x = 10 # Global variabel

def visa_siffror():

y = 5 # Lokal variabel

print("Inuti funktionen:", x, y)

visa_siffror()

print("Utanför funktionen:", x)

print(y) # Detta skulle ge ett NameError!

När `visa_siffror()` körs kan den läsa både `x` och `y`. Men försöker vi komma åt `y` utanför funktionen kraschar programmet eftersom `y` inte existerar i den globala räckvidden.

LEGB-regeln: Hur Python söker efter namn

När du använder en variabel i Python måste tolkaren ta reda på vilket värde variabelnamnet hänvisar till. Python följer en mycket specifik ordning för denna namnuppslagning, känd som LEGB-regeln.

Ordningen sökningen sker i är:

1. L – Local (Lokal): Python kollar först om namnet finns inuti den aktuella funktionen (inklusive parametrar).

2. E – Enclosing (Omslutande): Om funktionen ligger inuti en annan funktion (nästlad funktion), söks variabeln i den yttre funktionens räckvidd.

3. G – Global (Global): Om variabeln inte hittas i lokala eller omslutande räckvidder, söker Python i modulens (filens) toppnivå.

4. B – Built-in (Inbyggd): Sista anhalten är Pythons inbyggda namnrymd, där standardfunktioner som `len()`, `print()`, `range()` och feltyper finns.

Om variabeln inte hittas i någon av dessa fyra nivåer kastar Python ett `NameError`.

Ett viktigt koncept är att Python stannar så fort variabelnamnet hittas. Om du har en lokal variabel med samma namn som en global variabel, kommer den lokala variabeln att "skugga" (*shadow*) den globala variabeln inuti funktionen.

Modifiera globala variabler och UnboundLocalError

Att läsa en global variabel inuti en funktion går bra utan några konststigheter. Men vad händer när du försöker ändra (tilldela ett nytt värde till) en global variabel inuti en funktion?

python

poäng = 0

def öka_poäng():

poäng = poäng + 1 # Detta orsakar fel!

öka_poäng()

När du kör koden ovan får du felmeddelandet `UnboundLocalError: local variable 'poäng' referenced before assignment`.

Varför sker detta?

När Python ser en tilldelningsoperator (`=`) inuti en funktion antar den automatiskt att variabeln till vänster om `=` är en lokal variabel. I raden `poäng = poäng + 1` försöker Python alltså utvärdera högerledet `poäng + 1` genom att slå upp den lokala variabeln `poäng`, som ännu inte har fått något värde!

Nyckelordet `global`:

För att berätta för Python att du vill modifiera den globala variabeln och inte skapa en ny lokal variabel, måste du använda nyckelordet `global`:

python

poäng = 0

def öka_poäng():

global poäng # Deklarera att vi använder den globala variabeln

poäng = poäng + 1

öka_poäng()

print(poäng) # Skriver ut 1

Bästa praxis: Undvik globala variabler

Även om nyckelordet `global` finns i Python rekommenderar erfarna programmerare att man undviker det så mycket som möjligt. Att förlita sig på globala variabler leder ofta till så kallad "spagettikod" och svårhittade buggar.

Nackdelar med globala variabler:

  • Svårspårade sidoeffekter: Vilken funktion som helst i hela programmet kan ändra variabelns värde, vilket gör det svårt att veta varför ett värde blev fel.
  • Svårt att testa: Funktioner som beror på globala tillstånd är svåra att testa isolerat i enhetstester.
  • Dålig återanvändbarhet: Du kan inte enkelt kopiera funktionen till ett annat script utan att ta med alla globala variabler den beror på.

Den rätta lösningen: Skicka parametrar och returnera värden

Istället för att ändra globala variabler inuti funktioner bör du passera data som funktionens argument och returnera det nya värdet med `return`.

Dålig stil (med `global`):

python

total = 0

def lägg_till(pris):

global total

total += pris

Bra stil (ren funktion):

python

def lägg_till(aktuellt_total, pris):

return aktuellt_total + pris

Användning:

total = 0

total = lägg_till(total, 50)

total = lägg_till(total, 30)

Genom att använda argument och returvärden blir funktionen helt fristående och förutsägbar.

Hela kursen