Tilbake
5.1

5.1 Klasser og objekter: `class`, `__init__` og `self`

Definere en klasse, lage objekter, og sette instansvariabler i konstruktøren med `self._x` — kjernehåndverket i objektorientering, og der `self`-fella lever.

50 min
8 oppgaver
Klasserobjekter`class``__init__``self`
Din fremgang i kapitlet
0 / 8 oppgaver

Forkunnskaper — sist du var her

Dette kapitlet bygger direkte på
kap. 2.2 — Funksjonssamarbeid og scope, og indirekte på kap. 2.1 — Funksjoner og
kap. 3.1 — Lister.

Har du aldri programmert før, finnes en mykere inngang til de aller første begrepene i Programmering med Python (1P).

De to tingene fra kap. 2.2 som avgjør alt her, friskes opp med kjørt kode.

1. En variabel inne i en funksjon forsvinner når funksjonen er ferdig. Det
kalles at den er lokal, og det er nettopp det self-fella handler om:

def lag_teller():
    antall = 0 antall = antall + 5 print("inne i funksjonen:", antall)

lag_teller()
print("etter kallet finnes ikke antall lenger")

Utskrift:

inne i funksjonen: 5 etter kallet finnes ikke antall lenger

2. En parameter er et navn funksjonen bruker på verdien den fikk inn.
Verdien kommer utenfra, navnet lever bare inne i funksjonen:

def hils(navn):
    print("Hei,", navn)

hils("Ada")
hils("Nora")

Utskrift:

Hei, Ada
Hei, Nora

Begge deler kommer igjen om få minutter, med ett nytt tillegg: en måte å la verdiene overleve kallet på.

Løkke 1 — Fra løse variabler til ett objekt (~12 min)

En sykkelutleie har rundt tusen sykler ute i byen. Hver sykkel har en kode, et stativ den hører til, og en opplysning om den er utleid akkurat nå.

Med det du kan så langt, ville du holdt styr på det med tre lister som må holdes i takt: én med koder, én med stativer, én med utleid-eller-ikke. Det virker helt til noen setter inn en sykkel i den ene lista og glemmer de to andre. Da er sykkel nummer 400 plutselig en annen sykkel i hver liste.

Objektorientering er svaret på akkurat det problemet. I stedet for tre lister
som må holdes i takt, lager du én ting — ett sykkel-objekt — som bærer alle tre opplysningene sine selv, og som kan gjøre noe med dem.

Klasse

En klasse er en beskrivelse av hvordan en bestemt slags ting ser ut og hva den kan gjøre. Den er ikke tingen selv.

Tenk på skjemaet en sykkelutleie fyller ut for hver ny sykkel: feltene «kode»,
«stativ» og «utleid» står trykt på arket, likt for alle sykler. Klassen er
arket; hver utfylte kopi er en sykkel.

Klassen skrives én gang, og du kan lage så mange objekter du vil fra den. Det er hele poenget: én beskrivelse, mange konkrete ting.

Objekt (instans)

Et objekt er én konkret ting laget fra en klasse — én utfylt kopi av skjemaet. Ordet instans betyr nøyaktig det samme, og brukes om hverandre både i faget og i eksamensoppgavene.

Hvert objekt har sine egne verdier. Sykkelen med kode R31 og sykkelen med kode R48 er to objekter av samme klasse, men de deler ingenting: å leie ut den ene rører ikke den andre.

Når du leser en oppgavetekst, er det ofte lett å se hva som er hva: klassen er det generelle ordet («en sykkel»), objektet er den bestemte tingen («sykkelen med kode R31»).

`class Navn:`

Linjen som starter en klassedefinisjon. Alt som skal høre til klassen, skrives
innrykket under den:

class Sykkel:
    def __init__(self, kode):
        self._kode = kode

Klassenavnet skrives med stor forbokstavSykkel, Konto, Medlem. Det er en konvensjon, ikke en regel Python håndhever, men fasiten følger den, og den gjør koden lettere å lese: stor forbokstav betyr klasse, liten betyr variabel.

Selve linjen class Sykkel: gjør ingenting synlig. Den bare gjør navnet
Sykkel tilgjengelig, klart til å lage objekter av.

`def __init__(self, ...)` — konstruktøren
Konstruktøren er metoden som kjører automatisk hver gang du lager et nytt
objekt. Den heter alltid __init__, med to understreker på hver side.

Navn av den formen kalles spesialmetoder: du kaller dem aldri selv, Python kaller dem for deg på bestemte tidspunkter. __init__ kalles i det øyeblikket objektet oppstår.

Jobben dens er én ting: gi objektet startverdiene sine. Alt objektet skal huske, settes her.

class Sykkel:
    def __init__(self, kode, stativ):
        self._kode = kode self._stativ = stativ self._utleid = False

Parameterlista er (self, kode, stativ), men når du lager objektet, sender du bare to verdier: Sykkel("R31", "Blindern"). self fylles ut av Python.

📜Konstruktør-malen

Bokas niende kodemønster, og det første av klassemønstrene. Malen er kort nok til å huskes ordrett:

class Klassenavn:
    def __init__(self, verdi1, verdi2):
        self._felt1 = verdi1 self._felt2 = verdi2 self._felt3 = 0

Tre regler følger med, og de er hele mønsteret:

1. Alt objektet skal huske, settes i __init__ — også felt som ikke kommer
utenfra. self._utleid = False og self._antall_turer = 0 hører hjemme her, selv om ingen sender dem inn.
2. Hver eneste tilordning starter med self._. Uten self blir det en
lokal variabel som forsvinner (feilkode #1, self-fella).
3. Én understrek foran navnet. self._kode, ikke self.kode. Hvorfor,
handler kap. 5.2 om; her holder det å gjøre det likt hver gang, fordi det er formen fasiten bruker.

Skriver du de tre linjene mekanisk hver gang du starter en klasse, har du sikret deg mot den vanligste feilen i hele Oppgave 4.

✏️Eksempel 1: Klassen `Sykkel`, fra beskrivelse til to objekter

En sykkelutleie vil holde styr på hver sykkel med en kode, et stativ og en opplysning om den er utleid. Skriv klassen, lag to sykler, og få hver av dem til å skrive seg selv ut.

Beskrivelsen nevner tre opplysninger, så konstruktøren setter tre instansvariabler. Bare de to første kommer utenfra; den tredje har en naturlig startverdi:

class Sykkel:
    def __init__(self, kode, stativ):
        self._kode = kode self._stativ = stativ self._utleid = False

    def skriv(self):
        print("Sykkel", self._kode, "i stativ", self._stativ)

sykkel1 = Sykkel("R31", "Blindern")
sykkel2 = Sykkel("R48", "Majorstuen")
sykkel1.skriv()
sykkel2.skriv()

Utskrift:

Sykkel R31 i stativ Blindern
Sykkel R48 i stativ Majorstuen

Les linjen sykkel1 = Sykkel("R31", "Blindern") sakte. Den gjør tre ting i
rekkefølge: Python lager et nytt, tomt objekt; kaller __init__ på det med
kode = "R31" og stativ = "Blindern"; og lar variabelen sykkel1 peke på det ferdige objektet.

Slik ville sensor sett på det: fire ting gir uttelling hver for seg her.
class-linjen med stor forbokstav, __init__ med self som første parameter, de tre self._-tilordningene, og metoden med self i signaturen. Hadde du glemt self._utleid = False, ville de tre andre fortsatt telt.

Legg merke til at skriv ikke tar imot noe utenfra. Den trenger det ikke — objektet bærer verdiene sine selv, og self gir metoden tilgang til dem.

📝Oppgave 1

(Innstegsoppgave — gjengivelse.) Under står klassen Boks fra en resirkuleringsstasjon.

class Boks:
    def __init__(self, materiale, vekt):
        self._materiale = materiale self._vekt = vekt self._tomt = True

a) Hvor mange instansvariabler har et Boks-objekt?

b) Hvor mange verdier sender du inn når du lager en boks?

c) Skriv linjen som lager en boks av glass med vekt 340.

Løkke 2 — self: navnet objektet bruker på seg selv (~13 min)

Klassen Sykkel skrives én gang, men brukes av tusen sykler. Når metoden
skriv kjører, må den vite hvilken av de tusen den snakker om.

Det er hele jobben til self. Ordet er ikke magi, og det er ikke et nøkkelord i
Python: det er bare navnet på den første parameteren i enhver metode — og
Python fyller den ut med objektet metoden ble kalt på.

`self`

Den første parameteren i enhver metode. Den peker på det objektet metoden ble kalt på.

Kaller du sykkel1.skriv(), er self inne i metoden det samme objektet som
sykkel1 peker på. Kaller du sykkel2.skriv(), er self sykkel nummer to.
Samme kode, ulikt objekt.

To ting følger av dette, og begge er eksamensstoff:

- self skrives alltid i metodens definisjon: def skriv(self):.
- self skrives aldri når du kaller: sykkel1.skriv(), ikke
sykkel1.skriv(self).

Python fyller inn self selv, ut fra hva som står foran punktumet.

Instansvariabel

En variabel som hører til objektet, ikke til metoden. Den skrives med self. foran, og i denne boka alltid med en understrek etter punktumet: self._kode.

Forskjellen fra en vanlig lokal variabel er at instansvariabelen overlever metodekallet. Den ligger i objektet og er der neste gang en metode kalles på det samme objektet.

To objekter av samme klasse har hver sine instansvariabler. sykkel1._kode og sykkel2._kode er to helt uavhengige verdier, selv om koden som satte dem er den samme.

Ordet attributt brukes om det samme i mange lærebøker. Kommer det i en oppgavetekst, betyr det instansvariabel.

✏️Eksempel 2: `self`-fella kjørt, side om side med den riktige koden

Vis hva som faktisk skjer når én av to instansvariabler settes uten self., og forklar hvorfor programmet ikke stopper.

Klassen under har fellen i seg: plass settes uten self. i konstruktøren, mens self._plass fikk startverdien 0 en linje før.

class Stativ:
    def __init__(self, navn, plasser):
        self._navn = navn self._plasser = 0 plasser = plasser

    def skriv(self):
        print(self._navn, "har", self._plasser, "plasser")

s = Stativ("Blindern", 24)
s.skriv()

Utskrift:

Blindern har 0 plasser

Programmet svarer feil uten å klage. Tallet 24 kom inn i konstruktøren, ble
lagt i den lokale variabelen plasser, og forsvant da konstruktøren var ferdig.
Igjen står startverdien 0.

Retter du den ene linjen, oppfører alt seg som ventet:

class Stativ:
    def __init__(self, navn, plasser):
        self._navn = navn self._plasser = plasser

    def skriv(self):
        print(self._navn, "har", self._plasser, "plasser")

s = Stativ("Blindern", 24)
s.skriv()

Utskrift:

Blindern har 24 plasser

Merk hvor liten forskjellen er i tegn, og hvor stor den er i konsekvens. Det
er derfor sensorene planter nettopp denne: den skiller den som har forstått hva
self gjør, fra den som har lært seg formen utenat.

📝Oppgave 2
Eksamensnivå, sjanger B

En brettspillklubb registrerer spill slik:

class Spill:
    def __init__(self, tittel, spillere):
        self._tittel = tittel self._spillere = 0 spillere = spillere

    def skriv(self):
        print(self._tittel, "for", self._spillere, "spillere")

sp = Spill("Kolonier", 4)
sp.skriv()

a) Hva skriver programmet ut?

b) Hvilken feilkode er dette, og hva er den presise grunnen?

c) Endre nøyaktig én linje slik at programmet skriver det som var ment.

📝Oppgave 3
Eksamensnivå, sjanger…

En frivilligsentral vil registrere hver frivillig med navn, telefonnummer og antall timer utført. Antall timer skal starte på 0 og ikke oppgis når den frivillige registreres.

a) Skriv klassen Frivillig med bare konstruktøren.

b) Skriv linjen som registrerer «Ada» med telefonnummer "90012345".

c) Hvorfor er telefonnummeret lagret som tekst og ikke som tall?

Løkke 3 — Metoder: det objektet kan gjøre (~13 min)

Så langt kan objektene våre huske. Nå skal de kunne gjøre noe.

En metode er en funksjon som hører til klassen. Den skrives innrykket inne i klassen, har self som første parameter, og kalles med punktum bak objektet:
sykkel1.skriv(). Alt du kan om funksjoner fra
kap. 2.1, gjelder uendret — parametere, return og det hele.

Når du leser en oppgavetekst, er metodene lette å finne: hver «kan gjøre»- setning blir en metode. «En sykkel kan leies ut», «en sykkel kan leveres tilbake», «en sykkel kan si hvor mange turer den har hatt» — tre setninger, tre metoder.

`def metode(self, ...)`

En funksjon som hører til klassen, skrevet innrykket inne i den, med self som
første parameter:

class Sykkel:
    def __init__(self, kode):
        self._kode = kode self._turer = 0

    def registrer_tur(self):
        self._turer = self._turer + 1

Etter self kommer eventuelle vanlige parametere: def sett_stativ(self, nytt).
De fungerer nøyaktig som i en vanlig funksjon.

Inne i metoden når du objektets verdier med self._navn. Uten self. foran lager du bare en lokal variabel som forsvinner — samme felle som i konstruktøren, og samme feilkode #1.

Metodekall `obj.metode(...)`

Slik du får et objekt til å gjøre noe: navnet på objektet, punktum, navnet på metoden, parentes — for eksempel sykkel1.registrer_tur().

self skrives ikke her. Python ser hva som står foran punktumet og sender
det inn som self automatisk. Skriver du sykkel1.registrer_tur(sykkel1), stopper programmet fordi metoden fikk ett argument for mye.

Regnestykket er alltid det samme: definisjonen har én parameter mer enn kallet har argumenter. Den ekstra er self.

Skal metoden gi deg en verdi tilbake, bruker den return, og du tar imot resultatet som fra en vanlig funksjon: n = sykkel1.hent_turer().

✏️Eksempel 3: `Sykkel` med metoder som endrer og metoder som svarer

Utvid Sykkel med en metode for å leie ut, en for å levere tilbake, og en som svarer på hvor mange turer sykkelen har hatt. Vis at metodene virker på hvert sitt objekt.

Tre «kan gjøre»-setninger blir tre metoder. To av dem endrer objektet, den tredje svarer med return. Under klassen står bruken av den, slik at du ser begge deler i sammenheng — og slik at sykkel2 kan stå der urørt til sammenligning:

class Sykkel:
    def __init__(self, kode, stativ):
        self._kode = kode self._stativ = stativ self._utleid = False self._turer = 0

    def leie_ut(self):
        self._utleid = True self._turer = self._turer + 1

    def lever_tilbake(self, nytt_stativ):
        self._utleid = False self._stativ = nytt_stativ

    def hent_turer(self):
        return self._turer

    def skriv(self):
        print(self._kode, "i", self._stativ, "- utleid:", self._utleid)

sykkel1 = Sykkel("R31", "Blindern")
sykkel2 = Sykkel("R48", "Majorstuen")

sykkel1.leie_ut()
sykkel1.lever_tilbake("Toyen")
sykkel1.leie_ut()

sykkel1.skriv()
sykkel2.skriv()
print("Turer:", sykkel1.hent_turer(), sykkel2.hent_turer())

Utskrift:

R31 i Toyen - utleid: True
R48 i Majorstuen - utleid: False
Turer: 2 0

Tre ting å legge merke til.

For det første skriver print verdien True med stor forbokstav — det er slik
Python skriver sannhetsverdier, og det er verdt å huske til sporingsoppgavene.

For det andre er sykkel2 fullstendig uberørt av alt som skjedde med sykkel1.
De to objektene har hver sine instansvariabler. Det er kjernen i neste løkke.

For det tredje: hent_turer returnerer et tall, mens skriv skriver ut.
Forskjellen er den samme som mellom return og print i
kap. 2.1, og den er verdt å være nøye med. En metode som skriver ut i stedet for å returnere, kan ikke brukes videre i et regnestykke.

Merk stavemåten Toyen. Koden i denne boka bruker aldri æ, ø eller å, fordi
enkelte eksamensmiljøer har hatt problemer med dem. Norske navn på variabler og metoder er derimot fasitens egen stil.

📝Oppgave 4
Eksamensnivå, sjanger…

En kantine registrerer hver rett med navn, pris og antall solgte porsjoner (som starter på 0).

a) Skriv klassen Rett med konstruktør.

b) Legg til metoden selg(self, antall) som øker antall solgte.

c) Legg til metoden hent_omsetning(self) som returnerer pris ganger antall
solgte.

d) Skriv kode som lager retten «Fiskesuppe» til 89 kroner, selger 3 og så 2
porsjoner, og skriver ut omsetningen.

📝Oppgave 5
Eksamensnivå, sjanger A

Hva skriver programmet ut? Skriv utskriften nøyaktig, med mellomrom og linjeskift slik terminalen viser den.

class Teller:
    def __init__(self, start):
        self._verdi = start

    def opp(self):
        self._verdi = self._verdi + 1

    def hent(self):
        return self._verdi

a = Teller(5)
b = Teller(5)
a.opp()
a.opp()
b.opp()
print(a.hent(), b.hent())
print(a.hent() + b.hent())

Løkke 4 — Mange objekter av samme klasse (~10 min)

Sykkelutleien har tusen sykler, ikke to. Poenget med en klasse er nettopp at den skaleres: du skriver beskrivelsen én gang og lager så mange objekter du trenger
— i en løkke, om du vil.

Objektene kan legges i en liste, akkurat som tall og tekster i
kap. 3.1. Da kan du løpe gjennom dem og kalle den samme metoden på hver enkelt. Det er hele grunnstammen i Oppgave 4, som du møter for alvor i kap. 6.2.

Hvert objekt har sine egne instansvariabler

Å lage to objekter av samme klasse gir to helt uavhengige sett med verdier.
Klassen er delt; verdiene er ikke.

class Konto:
    def __init__(self, eier):
        self._eier = eier self._saldo = 0

    def sett_inn(self, belop):
        self._saldo = self._saldo + belop

    def hent_saldo(self):
        return self._saldo

k1 = Konto("Ada")
k2 = Konto("Nora")
k1.sett_inn(100)
print(k1.hent_saldo(), k2.hent_saldo())

Utskrift:

100 0

Dette er et fast spørsmål i flervalget om objekter, og svaret er alltid det samme: metoder virker bare på det objektet de ble kalt på.

✏️Eksempel 4: Mange objekter i en liste — mønsteret Oppgave 4 bygger på

En brettspillklubb har flere spill. Lag tre Spill-objekter, legg dem i en liste, skriv ut alle, og finn hvor mange av dem som passer for minst fire spillere.

Klassen er kort med vilje — tyngden ligger i hva vi gjør med objektene etterpå. Under klassen lager vi tre objekter, legger dem i en liste, og bruker to av mønstrene fra del 3 — gjennomløping og teller — med objekter i stedet for tall:

class Spill:
    def __init__(self, tittel, maks):
        self._tittel = tittel self._maks = maks

    def hent_maks(self):
        return self._maks

    def skriv(self):
        print(self._tittel, "- inntil", self._maks, "spillere")

spillene = []
spillene.append(Spill("Kolonier", 4))
spillene.append(Spill("Ordjakt", 6))
spillene.append(Spill("Sjakk", 2))

for s in spillene:
    s.skriv()

antall = 0 for s in spillene:
    if s.hent_maks() >= 4:
        antall = antall + 1 print("Passer for minst fire:", antall)

Utskrift:

Kolonier - inntil 4 spillere
Ordjakt - inntil 6 spillere
Sjakk - inntil 2 spillere
Passer for minst fire: 2

Legg merke til at løkkene er nøyaktig de samme som i del 3. Den eneste
forskjellen er at elementene er objekter, så du henter verdien med et metodekall
(s.hent_maks()) i stedet for direkte. Teller-mønsteret — antall = 0 før løkka, + 1 inne i den, bruk etter — er uendret.

Slik ville sensor sett på det: at du kaller en metode på hvert element i
stedet for å grave i verdiene selv, er nettopp det som gir uttelling i den store oppgaven. Hvorfor det er så viktig, forklarer
kap. 5.2.

— naturlig pausepunkt —

📝Oppgave 6
Eksamensnivå, sjanger A

Hva skriver programmet ut?

class Rute:
    def __init__(self, nummer):
        self._nummer = nummer self._passasjerer = 0

    def paa(self, antall):
        self._passasjerer = self._passasjerer + antall

    def av(self, antall):
        self._passasjerer = self._passasjerer - antall

    def hent(self):
        return self._passasjerer

rutene = []
rutene.append(Rute(20))
rutene.append(Rute(31))

rutene[0].paa(14)
rutene[1].paa(9)
rutene[0].av(5)

total = 0 for r in rutene:
    total = total + r.hent()
print(rutene[0].hent(), rutene[1].hent())
print(total)

📝Oppgave 7
Eksamensnivå, sjanger F

Et treningssenter vil registrere hver sal med navn, kapasitet og antall påmeldte (som starter på 0).

a) Skriv klassen Sal med konstruktør og metodene meld_paa(self, antall)
og hent_ledige(self), der den siste returnerer hvor mange plasser som er igjen.

b) meld_paa skal ikke slippe inn flere enn det er plass til: er det ikke
plass til alle, skal ingen meldes på, og metoden skal skrive «Ikke plass».

c) Lag salen «Speilsal» med kapasitet 20, meld på 15, prøv å melde på 8, og
skriv ut antall ledige.

📝Oppgave 8
Eksamensnivå, sjanger B

Se på koden:

class Bok:
    def __init__(self, tittel):
        self._tittel = tittel self._utlaant = False

    def laan_ut(self):
        self._utlaant = True

    def er_utlaant(self):
        return self._utlaant

boker = []
boker.append(Bok("Sult"))
boker.append(Bok("Markens grode"))
boker[0].laan_ut()

a) Hva gir boker[1].er_utlaant()?

b) En medstudent påstår: «laan_ut endrer _utlaant for alle
Bok-objekter, siden det bare finnes én klasse.» Hva er galt med påstanden?

c) Hva ville skjedd om laan_ut var skrevet def laan_ut(self): med kroppen
utlaant = True?

Bør du kjenne til: klasser som bygger på andre klasser

Dette avsnittet er på bokas laveste prioritetsnivå, kjenne til. Du trenger det ikke for å svare på noe i settene vi har talt — men begrepet dukker opp i pensumboka, og da er det greit å vite hva det er.

En klasse kan defineres som en utvidelse av en annen: en Elsykkel kan bygge videre på Sykkel og få alt den har, pluss batterinivå. Mekanismen heter
arv, den skrives class Elsykkel(Sykkel):, og hjelpemetoden super() gir
tilgang til den opprinnelige konstruktøren. Både arv, super() og polymorfi er utenfor pensum i IN1000 og drilles ikke i denne boka; de hører hjemme i oppfølgeremnet IN1010, og de testes ikke på denne eksamenen.

Grunnen til at det er verdt å vite: ser du class X(Y): i en kodesnutt i et gammelt sett eller i pensumboka, skal du ikke bli redd. Det betyr bare at X også har alt Y har.

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.

Objektorientering

Måten å bygge et program på der data og det som gjøres med dataene, holdes samlet i objekter.

Alternativet — det du har gjort til nå — er å ha verdiene i løse variabler og lister, og funksjoner ved siden av som tar dem inn. Det virker helt til et program blir stort nok til at flere lister må holdes i takt.

IN1000 heter «Introduksjon til objektorientert programmering» fordi dette er fagets hovedidé. Fra og med dette kapitlet er det den formen alt bygges i.

Instansiering

Å lage et objekt fra en klasse: sykkel1 = Sykkel("R31", "Blindern").

Tre ting skjer i den ene linjen: Python lager et nytt tomt objekt, kaller
__init__ på det med verdiene du oppga, og lar variabelen peke på resultatet.

Ordet dukker opp i oppgavetekster i formen «instansiér tre objekter av klassen
…». Det betyr bare: lag tre.

Konstruktør

Metoden __init__, som kjører automatisk når et objekt lages.

Den har alltid self som første parameter, og jobben er å gi objektet startverdiene sine. Alt objektet skal huske, settes her — også felt som ikke kommer utenfra.

Konstruktøren returnerer ingenting. Skriver du return med en verdi i den, stopper programmet.

Attributt

Et annet ord for instansvariabel: en verdi som hører til objektet, satt med
self._navn.

Ordet er verdt å kjenne fordi Pythons egen feilmelding bruker det:
AttributeError: 'Medlem' object has no attribute '_alder' betyr «dette objektet har ingen instansvariabel som heter _alder».

Ser du den meldingen, er svaret nesten alltid feilkode #1: linjen i konstruktøren manglet self. foran.

Spesialmetode

En metode med to understreker på hver side av navnet, som Python kaller automatisk på bestemte tidspunkter — du kaller den aldri selv.

I denne boka finnes bare to: __init__, som kjører når objektet lages, og
__str__, som kjører når objektet skal skrives ut. Den siste møter du i
kap. 5.3.

Python har mange flere, men de er ikke pensum her.

Klassekropp og innrykk

Alt som hører til klassen, står innrykket under class Navn: — fire mellomrom, som ellers i boka.

Metodene rykkes inn ett hakk fra class-linjen, og innholdet i hver metode ett hakk til. En linje som ved et uhell mister innrykket, faller ut av klassen og blir en vanlig funksjon.

Feilkode #16 dekker nettopp dette: innrykksfeil og blandet tabulator og mellomrom. I en klasse er den ekstra lett å gjøre, fordi det er to nivåer å holde styr på.

Klassenavn med stor forbokstav

Konvensjonen som skiller klasser fra alt annet: Sykkel, Konto, Medlem — mens variabler og metoder skrives med liten.

Python krever det ikke, men fasiten følger det, og det gjør koden lettere å lese: står det stor forbokstav foran en parentes, lages et objekt.

Består navnet av flere ord, settes de sammen uten mellomrom og med stor forbokstav på hvert: TreningsSal eller Treningssal. Begge former finnes;
vær konsekvent innenfor din egen besvarelse.

Tilstand

De verdiene et objekt har akkurat nå — til sammen alle instansvariablene.

En sykkel med koden R31, stativet Toyen og _utleid lik True er i én tilstand. Kaller du lever_tilbake("Blindern"), endres tilstanden, men det er fortsatt samme objekt.

Det er dette som skiller et objekt fra en ren verdi: objektet husker mellom kallene. Et tall gjør ikke det.

Metode mot funksjon

En funksjon står for seg selv og kalles med navnet sitt: len(tekst). En
metode hører til noe og kalles med punktum: sykkel1.skriv().

Forskjellen i koden er self. Metoden har objektet som første parameter og kan derfor bruke objektets verdier uten å få dem inn utenfra.

Du har allerede brukt metoder lenge uten å tenke over det:
liste.append(e) og tekst.rstrip() er metoder på lister og tekster. Nå skriver du dine egne.

Parameterlista i `__init__`

Alt som står etter self i def __init__(self, ...), er verdier som må sendes inn når objektet lages.

Regnestykket er alltid: antall parametere minus én er antall verdier du oppgir. Sender du feil antall, stopper programmet med en TypeError som sier hvor mange den ventet.

Rekkefølgen betyr alt. Boks("glass", 340) og Boks(340, "glass") er begge lovlige for Python — den andre gir bare et objekt med materiale 340 og vekt
«glass», uten en eneste feilmelding.

Startverdi i konstruktøren

En instansvariabel som ikke kommer utenfra, men som settes til en fast verdi:
self._utleid = False, self._turer = 0, self._medlemmer = [].

Kjenn dem igjen i oppgaveteksten på formuleringer som «starter på null», «er ikke utlånt når den registreres» eller «har ingen medlemmer til å begynne med».

At de faktisk settes, er et selvstendig poeng i Oppgave 4. Uten dem finnes ikke variabelen, og første metode som leter etter den, stopper programmet.

`None` som «ingen verdi ennå»

Den innebygde verdien som betyr «ingenting». Den brukes som startverdi når et felt skal peke på noe senere, men ikke har noe å peke på ennå:
self._beste = None.

Den er ikke det samme som 0 eller den tomme teksten. None betyr at det ikke finnes noen verdi; 0 er en helt vanlig verdi.

Du møter den igjen i finn-beste-mønsteret i
kap. 6.3, der None er det ærlige svaret på «hvem er best?» i en tom samling.

Å modellere

Å lese en beskrivelse og bestemme hvilke klasser den trenger, hva hver av dem skal huske, og hva de skal kunne gjøre.

Framgangsmåten er nesten mekanisk: substantivene blir klasser og instansvariabler, «kan gjøre»-setningene blir metoder. «Et treningssenter har saler. En sal har navn og kapasitet, og man kan melde seg på» gir to klasser, tre instansvariabler og én metode.

Hele kap. 6.1 handler om dette steget, som avgjør resten av Oppgave 4.

Objektreferanse

Det en variabel egentlig holder når den «inneholder» et objekt: en peker til objektet, ikke objektet selv.

Konsekvensen ser du først for alvor i
kap. 5.4: skriver du b = a, får du to navn på
samme objekt, ikke to objekter.

Foreløpig holder det å vite at et objekt kan ha flere navn samtidig, og at tallene og tekstene du er vant til, oppfører seg annerledes fordi de ikke kan endres.

Mange objekter av samme klasse

Klassen skrives én gang og brukes så mange ganger du vil — også i en løkke.

class Plass:
    def __init__(self, nummer):
        self._nummer = nummer

plassene = []
for i in range(3):
    plassene.append(Plass(i))
print(len(plassene))

Utskrift:

3

Hver runde lager et nytt objekt med sine egne instansvariabler. Det er nettopp denne skalerbarheten som gjør klasser verdt bryet: tusen sykler krever ikke tusen linjer kode.

Metoder som endrer mot metoder som svarer

To slags metoder, og forskjellen koster poeng.

En endrende metode skriver til en instansvariabel og returnerer ingenting:
def leie_ut(self): self._utleid = True. En svarende metode returnerer en verdi og lar objektet være i fred: def hent_turer(self): return self._turer.

Blander du dem — lar en svarende metode skrive ut i stedet for å returnere — kan verdien ikke brukes videre, og alt som bygger på metoden, faller sammen.
Formen er den samme som skillet mellom return og print i
kap. 2.1.

Å lese en klasse fra en oppgavetekst

Ferdigheten som avgjør Oppgave 2 og Oppgave 4: å se på ti linjer kode og med én gang vite hva objektene kan.

Framgangsmåten er fast. Finn class-linjen. Les __init__ og skriv opp hvilke instansvariabler som faktisk settes med self._ — og hvilke som mangler self og altså aldri kommer inn i objektet. Skriv så opp hver metode med hva den endrer og hva den returnerer.

Gjør du det på papir før du svarer, faller de fleste flervalgene av seg selv.

Konstruktør-malen

Klassemønsteret i kortform, verdt å kunne skrive fra bunnen:

class Klassenavn:
    def __init__(self, verdi1, verdi2):
        self._felt1 = verdi1 self._felt2 = verdi2 self._felt3 = 0

Blokken er merket som ikke-kjørbar fordi den er en mal med plassholdernavn, ikke et program.

Tre kontroller når du er ferdig: starter hver tilordning med self._? Er
alle opplysningene objektet skal huske, med? Har feltene som ikke kommer
utenfra, fått en startverdi?

Blankt svar i flervalg

Et flervalgsspørsmål som står ubesvart, gir null poeng — akkurat som et galt svar. Det er ingen straff for å gjette.

På denne eksamenen betyr det én ting: kryss alltid av. Oppgave 2 er rundt ni poeng fordelt på fire spørsmål, og et blankt felt er to poeng gitt bort gratis.

Klarer du å utelukke to av fire alternativer, er gjetningen mellom de to resterende verdt halvparten av poengene i snitt.

Repetisjon — de åtte 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.