Göm menyn

Funktioner

Parametrisering

Exempel 1:

Den första varianten av den här sången har vi sett förut (den hårdkodade). Den kommer alltid skriva ut "beer". I den andra varianten har vi parametriserat vilken dryck man kan dricka, så istället för att den alltid skriver ut beer skriver den ut det som man anropar funktionen med.

Genom att bryta ut funktionaliteter i separata funktioner och parametrisera dem kan de återanvändas.

Exempel 2:

Här kommer en annan variant med samma resultat. Vi kan återanvända farm_verse funktionen även om farm_song() är omskriven.

Exempel 3:

Poängen med funktioner

Varför använder vi funktioner, istället för att ha all programkod i en stor hög?

Genom att använda funktioner behöver vi inte upprepa programkod , vilket gör att koden blir kortare och enklare. Detta gör koden lättare att testa och underhålla.

En annan fördel med att använda funktioner är att vi skapar ordning i koden. Genom att ha bättre ordning blir koden mer läsbar, främst genom beskrivande variabelnamn och funktioner (självdokumenterande kod). Grupperar man funktioner med likartade funktionalitet blir överblickbarheten bättre (modularisering).

Vad händer vid ett funktionsanrop?

Vi använder oss av exempel 2. Vad händer egentligen när vi anropar farm_verse("cow", "moo") på första raden i farm_song?

  1. De aktuella parametrarna beräknas ("cow och "moo")
  2. De formella parametrarna (animal och noise) tilldelas dessa värden
  3. farm_verse kropp körs, med andra ord alla print-anrop
  4. Programkontrollen lämnas tillbaks till punkten efter anropet (rad nr. 10)

Parameteröverföring

Nu ska vi kolla på hur parametrarna överförs vid ett funktionsanrop.

Exempel 1

Notera att 1000 skrivs ut när vi anropar test(), inte 1050 (se körexempel nedan). Detta beror på att det är en kopia av balance som skickas till funktionen add_interest, inte en referens till samma värde.

Alt text

Exempel 2

Om vi vill att add_interest ska ändra på balance får vi returnera det nya värdet ifrån add_interest. Sedan får vi skriva över värdet i balance med värdet vi fick tillbaka. Här kommer ett exempel på hur det går till.

Kör vi denna koden kan vi nu se att värdet på balance faktiskt har ändrats.

Exempel 3

När vi istället jobbar med sammansatta datatyper så beter sig Python lite annorlunda. Sammansatta datatyper överförs som referenser istället för kopior, som vi kommer se i exemplet nedan. Om vi vill att add_interest ska hantera listor behöver vi alltså inte returnera något. OBS: Detta kan ofta leda till underliga effekter ifall du använder destruktiva funktioner, då listan du skickar till en funktion sedan också påverkas utanför.

Kör vi test() ser vi nu att värdet har ändrats även om add_interest aldrig returnerade något.

Sammanfattningsvis kan vi säga att en enkel datatyp överförs som kopior, medans sammansatta datatyper överförs som en referens. Modifierar du en parameter som är en sammansatt datatyp så kommer den även att ändras utanför funktionen.

Parameteröverföringsmodeller

Programmeringsspråk har olika sätt att överföra parametrar, dessa kallas parameteröverföringsmodeller. Här är ett par av de vanligaste parameteröverföringsmodellerna:

  • Call-by-reference innebär att det som kommer in till funktionen är referenser till data, och att funktionen mycket väl kan ändra detta.
  • Call-by-value innebär att man beräknar det som skickas in till funktionen och skickar värdet.

Python använder en kombination av dessa två. Call-by-reference passar inte in på enkla datatyper, då dessa skickas som kopior. Däremot fungerar överföring av sammansatta datatyper på detta sätt. När det kommer till enkla datatyper passar Call-by-value bättre.

Synlighet

Vi har redan märkt att en funktions parametrar och lokala variabler existerar enbart så länge funktionen körs. Mer allmänt kan man tala om en symbols synlighet eller räckvidd och menar då den del av koden där symbolen existerar och kan användas. Detta gäller både variabler och funktioner. På engelska benäms begreppet scope eller ibland (scope of) visibility. Python använder, som många andra språk, lexikalisk räckvidd (eng. lexical scoping) ibland även kallat statisk räckvidd, vilket innebär att en symbols synlighet kan avgöras enbart genom att titta på källkoden.

Python har fyra nivåer av synlighet, d.v.s när Python behöver veta vilket värde en symbol har finns det fyra nivåer att söka igenom. Minnesregeln för att komma ihåg dessa är LEGB.

  • L (Local) innebär att man söker igenom den aktuella funktionen, dess parametrar och lokala variabler.
  • E (Enclosing) innebär att man söker igenom funktioner som finns en eller flera nivåer utanför den aktuella funktionen.
  • G (Global) innebär att man tittar efter symbolen på toppnivå i den aktuella modulen/filen.
  • B (Built-in) innebär att man söker igenom de inbyggda symbolerna i Python.
  • Om symbolen inte återfinns på någon av nivåerna signalerar Python ett fel.

Såhär fungerar olika nivåer av synlighet:

Skuggning

Den här koden kommer att skriva ut 25 om vi kör den. Vi har en lokal symbol i f som skuggar en global symbol med samma namn och gör att den inte är åtkomlig i den inre synlighetsnivån.

I en del andra språk öppnar for en ny nivå, det gör den inte i Python. Den sista print-satsen i koden nedanför kommer alltså att skriva ut "And now i is 2" eftersom 2 är det sista värdet som i har i for-loopen.

Tillhörande quiz

Finnes här


Sidansvarig: Peter Dalenius
Senast uppdaterad: 2016-08-15