1.6 Funksjoner, scope og lambda
Definere funksjoner, flere returverdier, default-argumenter, variabel-scope og lambda — inkludert «funksjoner som tar funksjoner».
def.- Sjanger A — kodesporing, altså «hva skriver dette programmet ut?». Her møter du den i sin lureste variant: scope-oppgaven, der en variabel har én verdi inne i en funksjon og en annen utenfor. Den er fast materiale på midtveiseksamen (UiOs digitale flervalgseksamen i oktober, som teller 25 % av karakteren).
- Sjanger E — «hvilken linje mangler?» / «hvilket funksjonskall er riktig?», der du får et program med ett hull og fire alternativer. Den kommer jevnlig og gir 1–2 poeng, og fasiten er nesten alltid en lambda.
- Og indirekte alt annet: hver eneste kodeskrivingsoppgave på den avsluttende eksamenen — rekker, klasser, ODE-løsere, fillesing — ber deg skrive en funksjon med riktig signatur og riktig returverdi.
Prioritet: dette må sitte (høyeste prioritet av bokas tre nivåer — «må sitte», «må kunne», «bør kjenne til»). Kapitlet er verktøykassa; resten av boka bruker den på hver eneste side.
- Kap. 1.1 — Variabler, datatyper, uttrykk og operatorpresedens — uttrykk, tilordning og presedens.
- Kap. 1.2 — Lister, indeksering og slicing — lister, som ofte er det funksjonene tar imot.
- Kap. 1.3 — Løkker — for, while, range og nøstede løkker — for-løkka og akkumulatormønsteret, som brukes inne i nesten hver funksjon.
- Kap. 1.4 — Strenger, f-strenger og formatert utskrift — f-strenger, til de formaterte tabellene i eksemplene.
- Kap. 1.5 — Dictionaries og nøstede dictionaries — begrepet tuppel, som er det du får når en funksjon returnerer flere verdier.
Har du aldri programmert før, er Programmering med Python en mykere inngang du kan bla gjennom først.
Løkke 1 — Å definere og kalle en funksjon (~10 min)
En værstasjon rapporterer i celsius, men skal levere tall til en amerikansk database i fahrenheit. Omregningen er den samme hver gang, og du trenger den på tjue steder i programmet. Da skriver du den én gang og gir den et navn.
Det er alt en funksjon er: en oppskrift med et navn, som tar imot noen verdier og leverer en verdi tilbake. På eksamen er det aldri spørsmål om om du skal skrive en funksjon — oppgaven sier «skriv en funksjon som …». Spørsmålet er om signaturen og returverdien blir riktige.
Definerer en funksjon. Linja slutter med kolon, og alt som hører til funksjonen står med innrykk under — nøyaktig samme regel som for løkkekroppen i kap. 1.3.
Navnene i parentesen kalles parametre. De er navn funksjonen bruker internt, og de får verdier først når funksjonen kalles.
def celsius_til_fahrenheit(grader):
return grader * 9 / 5 + 32
print(celsius_til_fahrenheit(20))
print(celsius_til_fahrenheit(-40))Utskrift:
68.0
-40.0Legg merke til at def-linja i seg selv ikke gjør noe: den lagrer oppskriften. Først kallet celsius_til_fahrenheit(20) kjører kroppen. Og at svaret er 68.0 og ikke 68 skyldes /, som alltid gir flyttall.
Leverer en verdi ut av funksjonen — og avslutter den umiddelbart. Alt som står etter return i samme funksjon, kjøres aldri.
En funksjon uten return gir verdien None, Pythons ord for «ingenting». Det er en av de vanligste sporingsfellene, fordi funksjonen kan se ut som den virker: den skriver noe ut, men leverer ikke noe tilbake.
def energi(masse, fart):
e = 0.5 * masse * fart**2
return e
print('denne linja kjores aldri')
print(energi(2.0, 3.0))Utskrift:
9.0Sensor skiller nøye mellom «funksjonen skriver ut svaret» og «funksjonen returnerer svaret». Ber oppgaven om at funksjonen skal beregne noe, skal den returnere — ikke skrive ut.
En funksjon som bare skriver ut, leverer likevel en verdi tilbake: None. Skriver du den ut, står det None.
def skriv_rapport(navn, grader):
print(navn, 'malte', grader, 'grader')
svar = skriv_rapport('Blindern', 4.2)
print(svar)Utskrift:
Blindern malte 4.2 grader
NoneTo linjer utskrift fra to ulike print. Den første kommer fra inne i funksjonen, den andre fra det funksjonen ga tilbake. Prøver du å regne videre med svar, stopper programmet med en TypeError — None kan ikke legges sammen med et tall.
def-linja; argumentet er verdien du sender inn i kallet.I def energi(masse, fart) er masse og fart parametre. I kallet energi(2.0, 3.0) er 2.0 og 3.0 argumenter.
Skillet er verdt å holde på fordi oppgavetekster bruker begge ordene, og fordi et parameternavn bare finnes inne i funksjonen. Det er det scope handler om, senere i kapitlet.
Den kinetiske energien til et legeme med masse og fart er . Skriv en funksjon som regner den ut, og bruk den til å lage en liten tabell for masse 2,0 kg ved fartene 1, 2 og 3 meter per sekund.
Funksjonen tar de to størrelsene formelen trenger, og returnerer resultatet. Tabellen lages med en løkke utenfor funksjonen — funksjonen skal beregne, ikke skrive ut:
def energi(masse, fart):
return 0.5 * masse * fart**2
for v in range(1, 4):
print(f'{v:3d}{energi(2.0, v):8.2f}')Utskrift:
1 1.00
2 4.00
3 9.00Tre ting sensor ser etter. Funksjonen har én oppgave og returnerer ett tall. Formelen står bare ett sted, så en rettelse virker overalt. Og løkkevariabelen v er et heltall fra range, mens energi gir flyttall — derfor 3d for det første og 8.2f for det andre i f-strengen, jf. kap. 1.4.
(Innstegsoppgave — ren gjengivelse og minimal sporing.) Se på funksjonen:
def omkrets(side):
return 4 * sideSvar på hvert punkt for seg.
a) Hva heter parameteren?
b) Hva skriver print(omkrets(3)) ut?
c) Hva skriver print(omkrets(2.5)) ut?
d) Hva ville print(omkrets(3)) skrevet ut hvis linja return 4 * side ble byttet ut med print(4 * side)?
Løkke 2 — Argumenter: posisjon, navn og standardverdier (~10 min)
En vindkjølingsformel trenger temperatur, vindstyrke og en faktor som nesten alltid er den samme. Da vil du kunne kalle funksjonen med to argumenter i det normale tilfellet, og med tre når faktoren skal endres.
Python gir deg tre måter å sende argumenter på, og de kan kombineres. Å vite nøyaktig hvilken verdi som havner hvor, er en fast kodesporingsøvelse.
Uten navn i kallet pares argumentene med parametrene i rekkefølge: første argument til første parameter, og så videre.
Det betyr at vindkjoling(4.0, 5.0) og vindkjoling(5.0, 4.0) er to helt forskjellige kall. Rekkefølgen i def-linja er kontrakten, og den må du lese før du kaller.
Skriver du parameternavn=verdi i kallet, spiller rekkefølgen ingen rolle: vindkjoling(vind=5.0, temp=4.0) er samme kall som vindkjoling(4.0, 5.0).
Regelen er at alle posisjonsargumenter må komme først i kallet; navngitte argumenter kan så stå i hvilken som helst rekkefølge etterpå.
Navngitte argumenter er sensorvennlige i lange kall: solve(t_span, N) sier lite, mens plt.plot(x, y, label='S') sier alt. Du møter formen igjen i kap. 4.3.
En parameter kan få en standardverdi i def-linja: def vindkjoling(temp, vind, faktor=0.7). Da kan argumentet utelates i kallet, og standardverdien brukes.
Parametre med standardverdi må stå sist i def-linja.
def vindkjoling(temp, vind, faktor=0.7):
return temp - faktor * vind
print(vindkjoling(4.0, 5.0))
print(vindkjoling(4.0, 5.0, 1.2))
print(vindkjoling(vind=5.0, temp=4.0))
print(vindkjoling(4.0, faktor=0.5, vind=5.0))Utskrift:
0.5
-2.0
0.5
1.5Linje 1 og 3 gir samme svar fordi de sender de samme verdiene — bare på hver sin måte. Standardverdier er hverdagskost i faget: steglengden h=1e-5 i numerisk derivasjon (kap. 6.1) og toleransen tol=1e-10 i testfunksjoner (kap. 3.1) er begge default-argumenter.
Feil #21 i bokas feilkatalog: en liste som standardverdi lages én gang, ved def-linja — ikke på nytt for hvert kall. Dermed deles den mellom kallene:
def logg(verdi, historikk=[]):
historikk.append(verdi)
return historikk
print(logg(1))
print(logg(2))
print(logg(3))Utskrift:
[1]
[1, 2]
[1, 2, 3]Nesten alle forventer [1], [2], [3]. Den trygge formen bruker None som standardverdi og lager lista inne i funksjonen:
def logg(verdi, historikk=None):
if historikk is None:
historikk = []
historikk.append(verdi)
return historikk
print(logg(1))
print(logg(2))Utskrift:
[1]
[2]Tall og strenger som standardverdi er alltid trygge — de kan ikke endres. Regelen gjelder bare lister og ordbøker.
En funksjon skal regne ut et vektet gjennomsnitt av noen målinger, der vekten normalt er 1,0. Skriv den, og vis hva som skjer når vekten dobles.
Vekten får en standardverdi, slik at det vanlige kallet blir kort:
def snitt(verdier, vekt=1.0):
total = 0
for v in verdier:
total += v * vekt
return total / len(verdier)
print(snitt([2, 4, 6]))
print(snitt([2, 4, 6], 2.0))Utskrift:
4.0
8.0Merk at total starter på 0 før løkka og at return står etter løkka, med mindre innrykk. Står return inne i løkka, avsluttes funksjonen etter første runde — det er en av de vanligste innrykksfeilene, og den er nummer #24 i feilkatalogen.
Variabelen heter total og ikke sum, fordi sum er navnet på en innebygd funksjon. Overskriver du det navnet, virker ikke sum(...) resten av programmet.
(Midtveisnivå, sjanger A — kodesporing.) Hva skriver programmet ut?
def f(a, b=2):
return a * b
print(f(3), f(3, 4), f(b=5, a=1))Temperaturen i en avkjølende kopp kaffe kan modelleres som at hver måling er grader over romtemperaturen, som normalt er 21,0 grader.
a) Skriv funksjonen over_rom(maalinger, rom=21.0) som tar en liste med temperaturer og returnerer en ny liste med hvor mange grader hver måling ligger over romtemperaturen.
b) Vis to kall: ett med standardverdien og ett der romtemperaturen er 18,0 grader.
Løkke 3 — Å returnere flere verdier (~8 min)
Noen ganger har en beregning to svar som hører sammen: minste og største måling, summen og gjennomsnittet, tidspunktene og løsningen. Da vil du levere begge fra samme funksjon.
Python gjør dette enklere enn de fleste språk, og formen møter du igjen i hver eneste ODE-oppgave i Del 7, der løseren returnerer både tidspunktene og løsningen.
Komma etter return pakker verdiene i en tuppel — den faste sammenstillingen du møtte i kap. 1.5. Funksjonen returnerer altså fortsatt bare én verdi; den verdien inneholder bare flere ting.
Tar du imot resultatet i ett navn, får du hele tuppelen, og kan indeksere i den som i en liste.
Med to navn på venstresiden pakkes tuppelen ut, ett navn per verdi. Antall navn må stemme eksakt med antall returverdier.
def min_og_maks(verdier):
minste = verdier[0]
storste = verdier[0]
for v in verdier:
if v < minste:
minste = v
if v > storste:
storste = v
return minste, storste
lav, hoy = min_og_maks([4.2, -1.5, 5.0, 0.3])
print(lav, hoy)
par = min_og_maks([4.2, -1.5, 5.0, 0.3])
print(par)
print(par[1])Utskrift:
-1.5 5.0
(-1.5, 5.0)
5.0Legg merke til forskjellen i utskriften: to navn gir to tall skilt med mellomrom, ett navn gir tuppelen med parenteser og komma. Det er en tegn-for-tegn-detalj i sjanger A.
Begge startverdiene settes til verdier[0], ikke til 0 — ellers ville en liste med bare negative tall gitt feil svar på storste.
Skriv en funksjon som tar en liste med målinger og returnerer summen, gjennomsnittet og antallet — i den rekkefølgen. Bruk den, og skriv gjennomsnittet med to desimaler.
def statistikk(verdier):
total = 0
for v in verdier:
total += v
return total, total / len(verdier), len(verdier)
s, snitt, n = statistikk([3, 4, 8])
print(s, n)
print(f'{snitt:.2f}')Utskrift:
15 3
5.00Tre navn på venstresiden, tre verdier på høyresiden — rekkefølgen er det som binder dem sammen, ikke navnene. Skriver du snitt, s, n = statistikk(...), får snitt summen, uten at Python protesterer. Det er derfor oppgaveteksten alltid sier hvilken rekkefølge returverdiene skal ha, og hvorfor du skal lese den setningen to ganger.
Merk også at 5.00 har to desimaler fordi f-strengen ber om det, mens det bakenforliggende tallet er 5.0.
(Midtveisnivå, sjanger A — kodesporing med flere returverdier.) Hva skriver programmet ut? Skriv utskriften nøyaktig, linje for linje.
def deler(tall):
return tall // 3, tall % 3
a, b = deler(11)
print(a, b)
svar = deler(11)
print(svar)
print(svar[0] + svar[1])Løkke 4 — Scope: hvor en variabel finnes (~12 min)
— naturlig pausepunkt —
Nå kommer kapitlets mest lønnsomme kvarter. Scope betyr «gyldighetsområde»: hvilken del av programmet et navn finnes i.
Regelen er kort, men konsekvensene er ikke intuitive, og nettopp derfor er scope en fast gjenganger på midtveiseksamen. Du får et program på fem linjer og skal si hva som skrives ut — og svaret avhenger av om en tilordning traff den globale variabelen eller lagde en ny, lokal en.
Et navn som får verdi inne i en funksjon, finnes bare der. Det forsvinner når funksjonen er ferdig, og kan ikke brukes utenfra.
Parametrene er også lokale: masse i def energi(masse, fart) finnes bare inne i energi.
Praktisk konsekvens: to funksjoner kan trygt bruke samme navn i, total eller x uten å ødelegge for hverandre.
Finnes ikke navnet lokalt, leter Python utenfor funksjonen. Derfor virker dette:
grense = 3
def over_grensen(verdi):
return verdi > grense
print(over_grensen(5), over_grensen(1))Utskrift:
True FalseFunksjonen leser grense utenfra, uten at den er sendt inn. Det virker, men det er dårlig vane: funksjonen blir avhengig av noe som står et helt annet sted. Sensor foretrekker at alt funksjonen trenger, kommer inn som argument — gjerne med standardverdi.
Dette er kjernen i feil #13. Et likhetstegn inne i en funksjon lager alltid en ny, lokal variabel — det endrer aldri den globale med samme navn.
x = 5
def f():
x = 10
return x
print(f(), x)Utskrift:
10 5Funksjonen returnerer sin egen x, som er 10. Den globale x er fortsatt 5. To forskjellige variabler, samme navn.
Merk rekkefølgen: print(f(), x) kaller funksjonen først, men det spiller ingen rolle — kallet kan ikke endre den globale x uansett når det skjer.
Regelen over gjelder tilordning. Endrer du innholdet i noe som ble sendt inn — med append, med indeksering eller med innsetting i en ordbok — ser du endringen utenfor også:
def legg_til_null(liste):
liste.append(0)
maalinger = [1, 2]
legg_til_null(maalinger)
print(maalinger)Utskrift:
[1, 2, 0]Men en tilordning inne i funksjonen kobler bare det lokale navnet til noe nytt, og lar originalen i fred:
def nullstill(liste):
liste = []
return liste
maalinger = [1, 2]
nullstill(maalinger)
print(maalinger)Utskrift:
[1, 2]Skillet er alt: liste.append(0) endrer objektet, liste = [] bytter ut navnet. Det er samme skille som ligger bak feil #20 i kap. 1.5 — å endre argumentet i stedet for å bygge og returnere noe nytt.
Feil #13 i bokas feilkatalog er å tro at en tilordning inne i en funksjon endrer den globale variabelen. Den har en ubehagelig variant: prøver du å lese og skrive samme globale navn i én setning, stopper programmet.
teller = 0
def registrer():
teller = teller + 1
return teller
print(registrer())Utskrift:
UnboundLocalError: local variable 'teller' referenced before assignmentDet er siste linje i feilmeldingen. Python ser likhetstegnet og bestemmer at teller er lokal i hele funksjonen — også på høyresiden, der den ennå ikke har fått noen verdi.
Den riktige formen i denne boka er alltid å sende inn og returnere: def registrer(teller): return teller + 1. Nøkkelordet global finnes, men er utenfor bokas stil og gir ingen ekstra uttelling.
Programmet under bruker navnet navn både utenfor og inne i en funksjon. Hva skriver det ut, og hvorfor?
navn = 'global'
def ytre():
navn = 'lokal'
print(navn)
ytre()
print(navn)Utskrift:
lokal
globalSporingen, linje for linje. Først settes den globale navn til 'global'. def-linja kjører ikke kroppen, bare lagrer den. Kallet ytre() lager en ny, lokal navn med verdien 'lokal', og skriver den ut. Når funksjonen er ferdig, forsvinner den lokale variabelen, og siste print finner den globale igjen, uforandret.
Mønsteret er hele scope-oppgaven på midtveis: to variabler med samme navn, og spørsmålet er hvilken av dem hvert print ser. Tegn to bokser — en for det globale nivået og en for funksjonen — og før hver tilordning inn i riktig boks. Da svarer du riktig hver gang.
(Midtveisnivå, sjanger A — scope.) Hva skriver programmet ut? Skriv utskriften nøyaktig, linje for linje.
faktor = 2
def skaler(verdi):
faktor = 10
return verdi * faktor
print(skaler(3))
print(faktor)
print(skaler(faktor))Hva skriver programmet ut? Skriv utskriften nøyaktig, linje for linje.
data = {'a': 1}
antall = 0
def oppdater(d, nokkel):
antall = 99
d[nokkel] = d.get(nokkel, 0) + 1
return antall
print(oppdater(data, 'a'), oppdater(data, 'b'))
print(data)
print(antall)Løkke 5 — Funksjoner som verdier, og lambda (~10 min)
Numerisk derivasjon, integrasjon og nullpunktsøking har én ting til felles: metoden er den samme uansett hvilken funksjon du behandler. Da vil du skrive metoden én gang og sende funksjonen inn som argument.
Det er dette som gjør at deriver(sin, 0.0) og deriver(kvadrat, 2.0) kan bruke samme kode. Og det er her lambda kommer inn: en måte å lage en liten funksjon på uten å gi den et eget navn med def.
dobbel er navnet på funksjonen; dobbel(7) er kallet. Uten parenteser får du selve funksjonen, og den kan lagres i en variabel og sendes videre som et hvilket som helst tall.def dobbel(x):
return 2 * x
g = dobbel
print(g(7))
print(type(dobbel))Utskrift:
14
<class 'function'>Parentesene er altså ikke pynt: de betyr «kjør nå». Å skrive plt.legend uten parenteser er en klassisk grunn til at ingenting skjer.
En parameter kan ta imot en funksjon, og kalles med parenteser inne i kroppen. Da er metoden skrevet én gang og virker for alle funksjoner:
from math import sin
def verditabell(f, a, b, n):
for i in range(n + 1):
x = a + i * (b - a) / n
print(f'{x:5.2f}{f(x):8.3f}')
verditabell(sin, 0.0, 1.0, 2)Utskrift:
0.00 0.000
0.50 0.479
1.00 0.841Merk range(n + 1): n er antall intervaller, og da trengs n + 1 punkter for å få med begge endene. Det er feil #1 i feilkatalogen — den hyppigste i hele faget — og du møter den igjen i hvert eneste kapittel om numeriske metoder.
Legg også merke til at sin sendes inn uten parenteser.
Lager en funksjon på én linje, uten def og uten return. lambda x: x**2 - 2 betyr nøyaktig det samme som:
def kvadrat(x):
return x**2 - 2Formen er lambda parametre: uttrykk. Etter kolon står ett uttrykk, som blir returverdien — ingen løkker, ingen flere linjer.
kvadrat = lambda x: x**2 - 2
print(kvadrat(3))
print(kvadrat(0))Utskrift:
7
-2Bruk lambda når funksjonen er kort og bare trengs på ett sted — typisk som argument til en annen funksjon. Skal den brukes flere steder, skriv def: det leses lettere.
Tar en tekst som inneholder Python-kode, regner den ut og gir verdien. Da kan et uttrykk komme utenfra — fra brukeren eller fra kommandolinjen — i stedet for å stå fast i koden.
x = 4
uttrykk = '3*x + 1'
print(eval(uttrykk))
print(eval('2**3**2'))Utskrift:
13
512Uttrykket regnes ut med de variablene som finnes akkurat da, og med vanlige presedensregler — derfor 512 og ikke 64, siden ** regnes fra høyre.
Kombinert med lambda gir dette et program som kan behandle en funksjon brukeren skriver inn: f = eval('lambda x: x**2 - 2'). Mer om brukerinput i kap. 2.1.
Feil #19 i bokas feilkatalog, og den er nesten alltid fasiten i sjanger E — «hvilken linje mangler?».
Skal noe sendes inn som en funksjon, må det være en funksjon. Dette virker ikke:
g = x**2 - 2 # feil: dette er et uttrykk, ikke en funksjon
verditabell(g, 0.0, 2.0, 4)Linja er ikke kjørbar slik den står — enten finnes ikke x ennå, og programmet stopper med NameError, eller så finnes x og g blir ett tall. Da stopper programmet i stedet inne i verditabell med en TypeError, fordi et tall ikke kan kalles med parenteser.
De to riktige formene er:
def g(x):
return x**2 - 2eller, på én linje:
g = lambda x: x**2 - 2Kjenner du igjen mønsteret «her skal det stå lambda», har du et lite poeng nesten gratis hvert år.
Bruk tabellfunksjonen fra definisjonen over til å lage en verditabell for på intervallet fra 0 til 2, med fire intervaller. Hvor skifter funksjonen fortegn?
Funksjonen trengs bare på dette ene stedet, så den skrives som en lambda rett inn i kallet:
def verditabell(f, a, b, n):
for i in range(n + 1):
x = a + i * (b - a) / n
print(f'{x:5.2f}{f(x):8.3f}')
verditabell(lambda x: x**2 - 2, 0.0, 2.0, 4)Utskrift:
0.00 -2.000
0.50 -1.750
1.00 -1.000
1.50 0.250
2.00 2.000Fortegnet skifter mellom og , altså mellom rad 3 og rad 4. Det er nøyaktig det signalet en nullpunktsmetode leter etter, og du møter det igjen i kap. 6.1.
Fire intervaller ga fem rader — feil #1 i praksis. Og verditabell ble ikke rørt: den samme koden virker for sin og for annengradsfunksjonen, fordi funksjonen kommer inn som argument. Det er hele poenget med å behandle funksjoner som verdier.
(Midtveisnivå, sjanger E — «hvilket funksjonskall er riktig?».) Funksjonen verditabell(f, a, b, n) fra eksempel 5 er definert. Programmet skal skrive en tabell for funksjonen fra 0 til 1 med to intervaller. Hvilken av linjene gjør det riktig?
| Alternativ | Linje |
|---|---|
| a | verditabell(3*x + 1, 0.0, 1.0, 2) |
| b | verditabell(lambda x: 3*x + 1, 0.0, 1.0, 2) |
| c | verditabell(lambda x: 3*x + 1(0.0), 1.0, 2) |
| d | verditabell(def g(x): return 3*x + 1, 0.0, 1.0, 2) |
Velg ett alternativ og begrunn hvorfor de tre andre er feil.
(Midtveisnivå, sjanger A — funksjon som argument.) Hva skriver programmet ut?
def bruk(func, verdi):
return func(verdi) + 1
print(bruk(lambda t: t * 10, 3))
print(bruk(abs, -5))En måling er «gyldig» dersom den oppfyller et krav som varierer fra oppgave til oppgave.
a) Skriv funksjonen tell_gyldige(verdier, krav) som tar en liste og en funksjon, og returnerer hvor mange av verdiene som oppfyller kravet. Funksjonen krav tar ett tall og returnerer True eller False.
b) Bruk den til å telle hvor mange av [4.2, -1.5, 5.0, 0.3, -0.8] som er over null, og hvor mange som er større enn 1.
c) Forklar hva som går galt hvis du kaller tell_gyldige(verdier, v > 0).
- #13 — variabel-scope. Et likhetstegn inne i en funksjon lager en lokal variabel og endrer aldri den globale. Å lese og skrive samme globale navn i én setning gir UnboundLocalError.
- #19 — uttrykk der en funksjon kreves. g = x**2 - 2 er ikke en funksjon. Skriv def eller lambda.
- #21 — mutabelt default-argument. def f(x, liste=[]) deler lista mellom alle kall. Bruk None som standardverdi og lag lista inne i funksjonen.
- Glemt return. Funksjonen gir None, og programmet stopper først senere, når du prøver å regne med svaret.
- Feil utpakking. Antall navn på venstresiden må stemme med antall returverdier, og rekkefølgen avgjør hva som havner hvor.
- return inne i løkka. Da avsluttes funksjonen etter første runde. return skal vanligvis stå med samme innrykk som løkka, ikke inne i den.
- Parenteser der de ikke skal være. deriver(sin, 0.0) sender funksjonen; deriver(sin(0.0), 0.0) sender et tall.
Begrepsbank
Begrepsbanken er flashcard-/repetisjonsstoff — den gjentar det du nettopp har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder kjernestoffet.
Funksjonens def-linje: navnet, parametrene, rekkefølgen deres og eventuelle standardverdier.
Oppgavetekster oppgir signaturen presist — «skriv funksjonen forward_euler(f, T, N, u0)» — og sensor sjekker den først. Bytter du om to parametre, virker koden fortsatt for deg, men ikke for den som kaller den. Les signaturen ordrett fra oppgaveteksten, og skriv den av.
En verdi er kallbar hvis den kan skrives med parenteser etter seg: funksjoner er kallbare, tall og lister er det ikke.
Ordet dukker opp i feilmeldingen TypeError: 'int' object is not callable, som nesten alltid betyr at du har sendt inn et tall der en funksjon var ventet — felle #19. I kap. 5.1 lager du dine egne kallbare objekter.
En liten funksjon som gjør én ting, brukt av en større. Sensor liker dem: de gjør koden kort, og de kan rettes hver for seg.
Et typisk eksempel er å skille beregningen fra utskriften — én funksjon som regner og returnerer, og løkka som skriver ut, utenfor. Da kan den samme beregningen brukes i en testfunksjon senere, slik du gjør i kap. 3.1.
Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.
Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av Universitetet i Oslo. Dette er ikke offisielt studiemateriell. Les mer.