Tilbake
5.5

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.

80 min
12 oppgaver
DRILLObjekt-flervalgreferanser`self`scope
Din fremgang i kapitlet
0 / 12 oppgaver

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 0

2. 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:

0

3. Grensesnittet er metodene, aldri understrek-variablene.

Har du de tre, er resten metode.

📜Løsningsoppskriften for Oppgave 2 — fem steg

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.

Fellekatalogen i Oppgave 2

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.

✏️Eksempel 1: Full Oppgave 2 med sensor-margnotater

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?

Steg 1 — hvilke instansvariabler settes faktisk?

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 er
self-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:

Linjeboks 1 (R31)boks 2 (R48)r31kopi_sykler
to Sykkel(...)R31/0R48/0boks 1
kopi = r31R31/0R48/0boks 1boks 1
tre sett_innR31/0R48/0boks 1boks 11, 2, 1
r31.sett_aar(2024)R31/2024R48/0boks 1boks 11, 2, 1
r31 = Sykkel("R99", 2025)R31/2024R48/0boks 3boks 11, 2, 1

Steg 4 — svarene.
2a) 0. Årstallet 2019 forsvant i self-fella, og r48 har aldri fått en
sett_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
3

2c) 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() og
skriv_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.

📝Oppgave 1
Eksamensnivå, sjanger B

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.

📝Oppgave 2
Eksamensnivå, sjanger B

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?

📝Oppgave 3
Eksamensnivå, sjanger B

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

📝Oppgave 4
Eksamensnivå, sjanger…

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.

📝Oppgave 5
Eksamensnivå, sjanger B

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

📝Oppgave 6
Eksamensnivå, sjanger B

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?

📝Oppgave 7
Eksamensnivå, sjanger B

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?

📝Oppgave 8
Eksamensnivå, sjanger B

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.

📝Oppgave 9
Eksamensnivå, sjanger…

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?

📝Oppgave 10
Eksamensnivå, sjanger B

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?

📝Oppgave 11
Eksamensnivå, sjanger…

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?

📝Oppgave 12
Eksamensnivå, sjanger B

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?

API- og konstruksjonsliste

Begrepsbank

Begrepsbanken er flashcard- og repetisjonsstoff — den gjentar det du nettopp har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder kjernestoffet.

Oppgave 2 — formen

É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.

De fem stegene

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.

`self`-fella med og uten startverdi

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.

`self`-fella i en metode

Fella lever ikke bare i konstruktøren. En metode som skal endre objektet, må skrive til self._x:

    def opp(self):
        n = self._n + 1

Blokken 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.

Boks og pil

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.

Å telle objekter i en snutt

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.

Dobbelttelling over nøkler

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 to
legg_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.

Rebinding i en snutt
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.

Scope i objektform

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.

Grensesnitt mot instansvariabel

Understreken bærer to budskap, og spørsmålene utnytter det.

Spørres det om instansvariabler, skal _kapasitet med — den settes med
self._ og hører til objektet.

Spørres det om grensesnittet, skal _kapasitet ikke med, men
hent_kapasitet() skal.

Les hvilket av de to ordene som står i spørsmålet. Det er den vanligste lesefeilen i 2d.

Feilkode #11 i en snutt
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.

Identitet mot likhet

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.

Objektreferanse mot lagret rådata
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.

Blankt svar

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.

Å lese alternativene bakfra

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.

Delvis uttelling i Oppgave 2

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.

Repetisjon — de sju setningene som må sitte

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.