5.5 DRILL — Objekt-flervalg: referanser, `self`, scope og grensesnitt
Full drill på Oppgave 2: én felles kodesnutt med to klasser, og flervalgsspørsmål om hva som skrives ut / hvilke påstander er sanne — referanser, `self`-fella, scope og grensesnitt.
Dette kapitlet trener Oppgave 2, og bare den. Sjangeren er B — flervalg om objekter, altså «hvilken påstand om denne koden er sann?» og «hva skrives ut?».
Sjanger B står i 6 av 6 moderne eksamenssett (100 %), alltid som deloppgave
2a–2d, til sammen rundt 8–9 poeng. Formen er lik hvert år: én felles kodesnutt med to klasser, og fire spørsmål til den.
Dette må sitte — bokas høyeste prioritetsnivå. Poengene er lette å ta når
metoden sitter, og lette å miste ellers, fordi hvert spørsmål er laget for å avsløre en bestemt misforståelse.
Blankt svar gir null poeng, akkurat som et galt svar. Det er ingen straff for
å gjette. Klarer du å utelukke to alternativer, er gjetningen mellom de to siste verdt halvparten av poengene i snitt — kryss alltid av.
Kapitlet er langt (~80 min). Det er delt i fire økter med tidsanslag, og du kan trygt ta én økt om gangen.
Forkunnskaper — sist du var her
Alt du trenger, står i de fire foregående kapitlene:
kap. 5.1 — Klasser og objekter,
kap. 5.2 — Innkapsling,
kap. 5.3 — __str__ og
kap. 5.4 — Referansesemantikk. I tillegg brukes scope-reglene fra kap. 2.2.
De tre tingene drillen hviler på, friskes opp med kjørt kode.
1. b = a gir to navn på samme objekt; b = Klasse() gir et nytt:
class Boks:
def __init__(self):
self._n = 0
def opp(self):
self._n = self._n + 1
def hent(self):
return self._n
a = Boks()
b = a c = Boks()
a.opp()
print(a.hent(), b.hent(), c.hent())Utskrift:
1 1 02. self-fella: en tilordning uten self. i konstruktøren blir lokal og
forsvinner:
class Hund:
def __init__(self, navn, alder):
self._navn = navn self._alder = 0 alder = alder
def hent_alder(self):
return self._alder
h = Hund("Rex", 4)
print(h.hent_alder())Utskrift:
03. Grensesnittet er metodene, aldri understrek-variablene.
Har du de tre, er resten metode.
Bruk den samme framgangsmåten hver gang. Den tar to–tre minutter og løser alle fire spørsmålene til den samme snutten.
Steg 1 — Les de to klassene og noter hvilke instansvariabler som faktisk
settes. Gå linje for linje gjennom hver __init__ og skriv opp bare de linjene som starter med self._. En linje som alder = alder skriver du ikke opp — den lager en lokal variabel som forsvinner. Det er self-fella, feilkode #1, og den er plantet i snutten omtrent annethvert år.
Steg 2 — Tegn objektene og pilene. Én boks per gang et klassenavn står med
parentes. Én pil per variabel og per instansvariabel som peker på et objekt.
Steg 3 — Spor snutten linje for linje, og oppdater tegningen. To slags
linjer, og forskjellen avgjør alt:
- a.metode(v) skriver i boksen. Alle som peker på den, ser det.
- a = ... flytter pilen. Ingen andre merker noe.
Steg 4 — Svar på hvert spørsmål ut fra tegningen slik den er akkurat da.
Spørsmålene kommer i rekkefølge gjennom snutten, så tegningen skal se ulik ut for
2a og 2d.
Steg 5 — For begrepsspørsmål: skill metodene fra variablene. Er spørsmålet om
grensesnittet, stryk alt med understrek. Er det om instansvariabler, er
det nettopp de som skal med.
Og til slutt, uansett: kryss av på alle fire. Blankt gir null.
De fem fellene som går igjen. Kjenner du dem igjen, vet du hva spørsmålet er ute etter før du har lest alternativene.
- self-fella (#1). En instansvariabel settes uten self., og aksessoren
svarer startverdien — eller programmet stopper, hvis ingen startverdi finnes.
- To navn på samme objekt (#2). b = a er ikke en kopi.
- Rebinding (#2). a = Klasse() flytter bare pilen; det gamle objektet lever
uendret videre.
- Scope (#12). En tilordning inne i en funksjon eller metode treffer bare det
lokale navnet.
- Grensesnitt (#11). Understrek-variabler er ikke del av grensesnittet, og
skal ikke røres utenfra.
Snutten på eksamen inneholder som regel to eller tre av dem samtidig.
Økt 1 — Den gjennomarbeidede casen (~15 min)
Under står en komplett Oppgave 2 slik den ser ut på eksamen: to klasser, én kodesnutt, fire spørsmål. Les den som om det var eksamensdagen, og bruk de fem stegene.
Gitt klassene og snutten under. Svar på 2a–2d.
class Sykkel:
def __init__(self, kode, aar):
self._kode = kode self._aar = 0 aar = aar
def hent_aar(self):
return self._aar
def sett_aar(self, nytt):
self._aar = nytt
def __str__(self):
return self._kode + "/" + str(self._aar)
class Stativ:
def __init__(self, sted):
self._sted = sted self._sykler = []
def sett_inn(self, s):
self._sykler.append(s)
def antall(self):
return len(self._sykler)
def skriv_alle(self):
for s in self._sykler:
print(s)
r31 = Sykkel("R31", 2021)
r48 = Sykkel("R48", 2019)
kopi = r31
st = Stativ("Blindern")
st.sett_inn(r31)
st.sett_inn(r48)
st.sett_inn(kopi)
r31.sett_aar(2024)
r31 = Sykkel("R99", 2025)2a) Hva gir r48.hent_aar()?
2b) Hva skriver st.skriv_alle() ut?
2c) Hva gir st.antall(), og hvor mange Sykkel-objekter er laget?
2d) Er _sykler del av grensesnittet til Stativ?
I Sykkel settes self._kode og self._aar, men self._aar settes til 0, og linjen aar = aar er en lokal tilordning som forsvinner. Det erself-fella, feilkode #1. Årstallene 2021 og 2019 kommer altså aldri inn i objektene.
I Stativ settes self._sted og self._sykler, begge riktig.
Steg 2–3 — tegningen, oppdatert linje for linje:
| Linje | boks 1 (R31) | boks 2 (R48) | r31 | kopi | _sykler |
|---|---|---|---|---|---|
to Sykkel(...) | R31/0 | R48/0 | boks 1 | — | — |
kopi = r31 | R31/0 | R48/0 | boks 1 | boks 1 | — |
tre sett_inn | R31/0 | R48/0 | boks 1 | boks 1 | 1, 2, 1 |
r31.sett_aar(2024) | R31/2024 | R48/0 | boks 1 | boks 1 | 1, 2, 1 |
r31 = Sykkel("R99", 2025) | R31/2024 | R48/0 | boks 3 | boks 1 | 1, 2, 1 |
Steg 4 — svarene.
2a)
0. Årstallet 2019 forsvant i self-fella, og r48 har aldri fått ensett_aar.2b) Sporet gjennom lista, som holder boks 1, boks 2 og boks 1 igjen:
class Sykkel:
def __init__(self, kode, aar):
self._kode = kode self._aar = 0 aar = aar
def sett_aar(self, nytt):
self._aar = nytt
def __str__(self):
return self._kode + "/" + str(self._aar)
class Stativ:
def __init__(self, sted):
self._sted = sted self._sykler = []
def sett_inn(self, s):
self._sykler.append(s)
def antall(self):
return len(self._sykler)
def skriv_alle(self):
for s in self._sykler:
print(s)
r31 = Sykkel("R31", 2021)
r48 = Sykkel("R48", 2019)
kopi = r31 st = Stativ("Blindern")
st.sett_inn(r31)
st.sett_inn(r48)
st.sett_inn(kopi)
r31.sett_aar(2024)
r31 = Sykkel("R99", 2025)
st.skriv_alle()
print(st.antall())Utskrift:R31/2024
R48/0
R31/2024
32c) st.antall() gir 3, men det er laget tre Sykkel-objekter — og
bare to av dem ligger i stativet. Lista har tre referanser til to objekter;
det tredje objektet (R99) ble laget etter at lista var fylt, og ligger ikke der.
2d) Nei. _sykler er en instansvariabel med understrek, altså innside.
Grensesnittet til Stativ er __init__(sted), sett_inn(s), antall() ogskriv_alle().
Sensors margnotater, steg for steg:
- 2a gir uttelling for å se self-fella, ikke for å skrive «0». Ser du at
linjen aar = aar er lokal, har du poenget.
- 2b rettes linje for linje. To riktige av tre linjer gir delvis uttelling.
- 2c er to spørsmål i ett, og de teller hver for seg. Å svare 3 på det første
og 2 på det andre er en klassisk halvbom — tell klassenavn med parentes for det andre.
- 2d er gratis når du kan regelen. Understrek betyr innside, uansett hvor
mange aksessorer som finnes.
- Blankt gir null. Er du usikker på 2c, kryss av likevel.
— naturlig pausepunkt —
Økt 2 — self-fella og scope (~20 min)
De fire første oppgavene trener steg 1 i oppskriften: å se hvilke instansvariabler som faktisk blir satt. Alle fire har en felle plantet i konstruktøren eller i en metode.
Gitt klassen:
class Medlem:
def __init__(self, navn, alder):
self._navn = navn alder = alder
def hent_navn(self):
return self._navn
def hent_alder(self):
return self._alder
m = Medlem("Ada", 24)a) Hva skjer ved kallet m.hent_alder()?
A. Det returnerer 0.
B. Programmet stopper med en feilmelding.
C. Det returnerer 24.
D. Det returnerer None.
Gitt klassen:
class Konto:
def __init__(self, eier, saldo):
self._eier = eier self._saldo = 0 saldo = saldo
def sett_inn(self, belop):
self._saldo = self._saldo + belop
def hent_saldo(self):
return self._saldo
k = Konto("Ada", 500)
k.sett_inn(200)a) Hva gir k.hent_saldo()?
A. 700
B. 500
C. Programmet stopper.
D. 200
b) Hvilken linje må endres, og til hva?
Gitt klassen:
class Teller:
def __init__(self):
self._n = 0
def opp(self):
n = self._n + 1
def ned(self):
self._n = self._n - 1
def hent(self):
return self._n
t = Teller()
t.opp()
t.opp()
t.ned()a) Hva gir t.hent()?
A. -1
B. 1
C. 2
D. 0
Gitt koden:
class Sal:
def __init__(self, navn):
self._navn = navn self._paa = 0
def meld_paa(self, n):
self._paa = self._paa + n
def hent(self):
return self._paa
def fyll(sal, antall):
sal.meld_paa(antall)
antall = 0
def bytt_ut(sal):
sal = Sal("Ny sal")
sal.meld_paa(99)
s = Sal("Speilsal")
fyll(s, 7)
bytt_ut(s)a) Hva gir s.hent()?
A. 0
B. 99
C. 7
D. 106
b) Hvor mange Sal-objekter er laget?
Økt 3 — Referanser, aliaser og rebinding (~22 min)
Nå trenes steg 2 og 3: tegningen, og skillet mellom å skrive i en boks og å flytte en pil. Dette er det tyngste stoffet i Oppgave 2 — bruk papir.
Gitt klassen:
class Merke:
def __init__(self, tekst):
self._tekst = tekst
def sett(self, ny):
self._tekst = ny
def __str__(self):
return self._tekst
a = Merke("rod")
b = a c = Merke("rod")
a.sett("gul")a) Hva skriver print(a, b, c) ut?
A. gul rod rod
B. gul gul rod
C. gul gul gul
D. rod rod rod
Gitt klassen Merke fra forrige oppgave, og snutten:
a = Merke("en")
b = a a = Merke("to")
b.sett("tre")a) Hva skriver print(a, b) ut?
A. to tre
B. tre tre
C. to to
D. en tre
b) Hvor mange Merke-objekter finnes etter de fire linjene?
Gitt to klasser:
class Bok:
def __init__(self, tittel):
self._tittel = tittel
def sett_tittel(self, t):
self._tittel = t
def __str__(self):
return self._tittel
class Hylle:
def __init__(self):
self._boker = []
def sett_inn(self, b):
self._boker.append(b)
def antall(self):
return len(self._boker)
def skriv_alle(self):
for b in self._boker:
print(b)
sult = Bok("Sult")
h1 = Hylle()
h2 = Hylle()
h1.sett_inn(sult)
h2.sett_inn(sult)
sult.sett_tittel("Sult (1890)")
sult = Bok("Vaar")
h1.sett_inn(sult)a) Hva skriver h1.skriv_alle() ut?
b) Hva gir h2.antall()?
c) Hvor mange Bok-objekter er laget?
d) Er _boker del av grensesnittet til Hylle?
Gitt to klasser der den ene holder en referanse til den andre:
class Eier:
def __init__(self, navn):
self._navn = navn
def hent_navn(self):
return self._navn
def sett_navn(self, n):
self._navn = n
class Konto:
def __init__(self, eier):
self._eier = eier self._eier_navn = eier.hent_navn()
def navn_via_referanse(self):
return self._eier.hent_navn()
def navn_via_kopi(self):
return self._eier_navn
ada = Eier("Ada")
k = Konto(ada)
ada.sett_navn("Ada Lovelace")a) Hva skriver print(k.navn_via_referanse(), "|", k.navn_via_kopi()) ut?
A. Ada | Ada Lovelace
B. Ada Lovelace | Ada Lovelace
C. Ada | Ada
D. Ada Lovelace | Ada
b) Hvilken av de to metodene er den riktige modelleringen, og hvorfor?
Økt 4 — Blandet, som på eksamen (~23 min)
De siste fire oppgavene blander fellene, slik snutten på eksamen gjør. Bruk alle fem stegene, og husk at spørsmålene til samme snutt rettes hver for seg.
Gitt klassen:
class Rute:
def __init__(self, nummer, kapasitet):
self._nummer = nummer self._kapasitet = kapasitet self._passasjerer = 0
def hent_nummer(self):
return self._nummer
def paa(self, n):
if n > self._kapasitet - self._passasjerer:
print("Fullt")
else:
self._passasjerer = self._passasjerer + n
def __str__(self):
return "Rute " + str(self._nummer)a) Hvilket alternativ beskriver grensesnittet til Rute riktig?
A. _nummer, _kapasitet, _passasjerer
B. __init__(self, nummer, kapasitet), hent_nummer(self), paa(self, n)
C. __init__(nummer, kapasitet), hent_nummer(), paa(n), __str__()
D. hent_nummer() alene, siden det er den eneste aksessoren
b) Er _passasjerer en instansvariabel?
Gitt to klasser:
class Spiller:
def __init__(self, navn, poeng):
self._navn = navn self._poeng = 0 poeng = poeng
def gi(self, n):
self._poeng = self._poeng + n
def hent_poeng(self):
return self._poeng
def __str__(self):
return self._navn + ":" + str(self._poeng)
class Lag:
def __init__(self):
self._spillere = []
def ta_opp(self, s):
self._spillere.append(s)
def totalt_antall_poeng(self):
sum_poeng = 0 for s in self._spillere:
sum_poeng = sum_poeng + s.hent_poeng()
return sum_poeng
ada = Spiller("Ada", 10)
nora = Spiller("Nora", 20)
lag = Lag()
lag.ta_opp(ada)
lag.ta_opp(nora)
ada.gi(5)
ada = Spiller("Ada2", 100)
ada.gi(50)a) Hva gir lag.totalt_antall_poeng()?
A. 5
B. 35
C. 85
D. 185
b) Hva skriver print(ada) ut?
c) Hvor mange Spiller-objekter er laget?
Gitt klassen:
class Stativ:
def __init__(self, sted, plasser):
self._sted = sted self._plasser = plasser self._sykler = []
def sett_inn(self, s):
if len(self._sykler) < self._plasser:
self._sykler.append(s)
else:
print("Fullt")
def antall(self):
return len(self._sykler)Et program bruker klassen slik:
st = Stativ("Blindern", 2)
st._sykler.append("R31")
st._sykler.append("R48")
st._sykler.append("R99")a) Hva gir st.antall()?
A. 2
B. 0
C. Programmet stopper.
D. 3
b) Hvilken feilkode er de tre linjene, og hva er den konkrete skaden?
c) Hvordan skulle programmet vært skrevet?
Gitt to klasser:
class Vare:
def __init__(self, navn, antall):
self._navn = navn self._antall = antall
def ta_ut(self, n):
self._antall = self._antall - n
def hent_antall(self):
return self._antall
def __str__(self):
return self._navn + "=" + str(self._antall)
class Lager:
def __init__(self):
self._varer = {}
def legg_inn(self, nokkel, vare):
self._varer[nokkel] = vare
def finn(self, nokkel):
if nokkel in self._varer:
return self._varer[nokkel]
return None
def totalt_antall(self):
sum_antall = 0 for k in self._varer:
sum_antall = sum_antall + self._varer[k].hent_antall()
return sum_antall
skrue = Vare("skrue", 10)
mutter = Vare("mutter", 6)
lager = Lager()
lager.legg_inn("skrue", skrue)
lager.legg_inn("A", skrue)
lager.legg_inn("mutter", mutter)
skrue.ta_ut(4)
skrue = Vare("skrue", 99)a) Hva gir lager.totalt_antall()?
A. 22
B. 18
C. 12
D. 111
b) Hva skriver print(lager.finn("A")) ut?
c) Hva skriver print(lager.finn("bolt")) ut, og hvorfor stopper ikke
programmet?
d) Hvor mange Vare-objekter er laget?
Fem feil koster nesten alle poengene som mistes i Oppgave 2:
- Å hoppe over steg 1. Leser du ikke konstruktøren nøye, ser du ikke
self-fella (#1), og da er hele sporingen bygget på gale startverdier.
- Å tro at b = a er en kopi (#2). Det er én ting med to navn.
- Å tro at en rebinding endrer objektet (#2). Den flytter én pil, og bare
den.
- Å tro at en funksjon kan flytte kallerens pil (#12). Den kan endre
objektet, ikke hvilket objekt kalleren peker på.
- Å blande instansvariabel og grensesnitt (#11). Understreken bærer begge
budskapene; les hvilket ord som står i spørsmålet.
Og én tellefeil: referanser er ikke objekter. En liste med fire elementer kan holde ett objekt. En sum over tre nøkler kan telle det samme objektet to ganger.
Og til slutt det billigste av alt: et blankt felt gir null poeng. Kryss alltid av.
Begrepsbank
Begrepsbanken er flashcard- og repetisjonsstoff — den gjentar det du nettopp har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder kjernestoffet.
Én felles kodesnutt med to klasser, og fire spørsmål (2a–2d) til den. Rundt 8–9 poeng, flervalg, i alle de seks moderne settene.
Spørsmålene følger snutten kronologisk, så tegningen din skal se ulik ut for 2a og 2d.
De rettes hver for seg. En bom på ett spørsmål koster ikke de tre andre — og blankt gir null, så kryss av på alle fire.
Oppskriften, i kortform: les konstruktøren, tegn, spor, svar, skill metoder fra variabler.
Steg 1 er det viktigste og det som oftest hoppes over. En plantet self-felle gjør alle startverdiene i sporingen gale.
Hele oppskriften tar to–tre minutter og brukes på alle fire spørsmålene til den samme snutten.
Feilkode #1 har to utfall, og du må se hvilket.
Uten startverdi: instansvariabelen finnes ikke i det hele tatt, og programmet
stopper med AttributeError: 'Klasse' object has no attribute '_x'.
Med startverdi: variabelen finnes, men fikk aldri verdien utenfra. Programmet
kjører rolig og svarer startverdien — 0, False eller den tomme lista.
Den andre varianten er den farlige, og den er den som plantes oftest.
Fella lever ikke bare i konstruktøren. En metode som skal endre objektet, må skrive til self._x:
def opp(self):
n = self._n + 1Blokken er et utdrag med en plantet feil og er merket som ikke-kjørbar.
Utregningen er riktig, men resultatet legges i en lokal variabel og kastes.
Metoden gjør ingenting, uten et eneste varsel.
Tegnemetoden som løser alle referansespørsmål. Én boks per objekt, én pil per variabel.
- b = a: la b-pilen peke der a-pilen peker.
- b = Klasse(): tegn en ny boks.
- a.sett_x(5): skriv 5 i boksen. Rør ingen piler.
To minutter på tegningen er raskere enn å lese koden tre ganger, og den brukes på alle fire spørsmålene.
Tell hvor mange ganger et klassenavn står med parentes bak seg. Det er antall objekter, uansett hvor mange variabler og listeplasser som peker på dem.
len(liste) teller referanser. En liste med fire elementer kan holde ett objekt.
Spørsmålet «hvor mange objekter er laget?» er et fast flervalg, og det tar ti sekunder når du vet hva du skal telle.
En ordbok kan ha to nøkler som peker på det samme objektet. En sum som løper over nøklene, teller da objektet to ganger.
Det er sjelden ønsket, og det er en ekte felle i sporingsoppgaver. Ser du tolegg_inn-kall med ulik nøkkel og samme objekt, er det plantet med vilje.
Skal hvert objekt telles én gang, må modellen sikre at det bare ligger under én nøkkel.
a = Klasse(...) etter at a allerede pekte på noe.Kjenn den igjen på at klassenavnet med parentes står på høyre side av likhetstegnet, og en variabel som allerede er i bruk, på venstre.
Etter linjen finnes ett objekt mer enn før, og alle andre piler til det gamle objektet er uendret. Det er nettopp derfor lister og ordbøker beholder det gamle.
En tilordning inne i en funksjon eller metode treffer bare det lokale navnet.
p = Bok("ny") inne i en funksjon gjør ingenting synlig utenfor. p.sett_x(...) gjør det, fordi den treffer objektet.
Grensen går ved punktumet: er det et likhetstegn rett etter navnet, er det lokalt; er det et metodekall, treffer det objektet. Dette er feilkode #12.
Understreken bærer to budskap, og spørsmålene utnytter det.
Spørres det om instansvariabler, skal _kapasitet med — den settes medself._ og hører til objektet.
Spørres det om grensesnittet, skal _kapasitet ikke med, menhent_kapasitet() skal.
Les hvilket av de to ordene som står i spørsmålet. Det er den vanligste lesefeilen i 2d.
obj._x eller obj._x = v utenfor klassen. Python stopper deg ikke, så programmet kjører og gir et svar.Skaden er at klassens egne regler hoppes over: en kapasitetssjekk, en telling eller to felter som skulle vært oppdatert sammen.
Ved «finn feilen»-spørsmål gir et konkret moteksempel full uttelling: hvilket kall som gir galt svar, og hva det riktige ville vært.
To objekter kan ha nøyaktig like verdier og likevel være to objekter.Merke("rod") to ganger gir to merker.
Identitet er «er dette det samme objektet?». Likhet er «har de samme
verdier?».
Oppgave 2 spør nesten alltid om identitet — formuleringene «peker på samme objekt», «hvor mange objekter finnes» og «sees endringen også via …» handler alle om det.
self._eier = person mot self._eier_navn = person.hent_navn().Den første leser navnet på nytt hver gang og holder seg i takt. Den andre lagret en tekst i konstruktøren, og teksten blir foreldet i det øyeblikket personen skifter navn.
Å lagre rådata i stedet for en objektreferanse er en av de faste vurderingspunktene i den store Oppgave 4.
Et ubesvart flervalg gir null poeng, akkurat som et galt svar. Det er ingen straff for å gjette.
Oppgave 2 er rundt ni poeng fordelt på fire spørsmål, så et blankt felt er over to poeng gitt bort gratis.
Klarer du å utelukke to alternativer, er gjetningen mellom de to siste verdt halvparten av poengene i snitt.
En praktisk teknikk når du står fast: se hva alternativene skiller seg på, og spor bare den detaljen.
Skiller de seg på ett tall, er det én linje i snutten som avgjør. Skiller de seg på om to navn er like, er det en referanse som er plantet.
Alternativene er laget ut fra fellekatalogen, så hvert av dem svarer til én bestemt misforståelse. Å vite hvilken, er ofte nok til å velge riktig.
Hvert av de fire spørsmålene rettes for seg. Det er ingen kjedereaksjon: bommer du på 2a, kan 2b, 2c og 2d fortsatt bli riktige.
Det betyr at det aldri lønner seg å stå fast. Sitter du fast på 2a, tegn ferdig og gå videre — spørsmålene handler ofte om ulike deler av snutten.
Og 2d er som regel et begrepsspørsmål som kan besvares uten å spore noe i det hele tatt.
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.