Objektorientering lar oss samle data og oppførsel i egne typer. En klasse er en mal, og et objekt er en konkret instans med egne attributter. Vi definerer metoder, bruker _init_ til å sette opp objektet og self til å referere til det. Dette stoffet er ensidig for TDT4109.
Læringsmål
01Forklare forskjellen på en klasse og et objekt, og at et objekt er en instans laget fra en klasse
02Skrive en klasse med _init_ som setter attributter via self, og lage objekter fra den
03Skrive og kalle metoder, og forklare hvordan self viser til objektet metoden kalles på
04Begrunne hvorfor objektorientering samler data og oppførsel, og bruke riktige navnekonvensjoner for klasser, metoder og _init_
Tenk deg at du skal holde styr på en konto i et lite bankprogram. En konto har et eiernavn og et saldobeløp, og du vil kunne sette inn og ta ut penger. Med det du har lært til nå, ville du kanskje lagt navnet i én variabel og saldoen i en annen, og skrevet løse funksjoner sett_inn(saldo, beløp) og ta_ut(saldo, beløp). Det virker — helt til du har hundre kontoer. Da flyter navn og saldoer rundt hver for seg, og det er lett å sende feil saldo inn i feil funksjon.
Objektorientering løser dette ved å pakke sammen det som hører sammen. En klasse er en oppskrift som sier: «en konto består av et navn og en saldo, og dette er tingene du kan gjøre med den». Ut fra oppskriften lager du så konkrete objekter — én Konto for Kari, én for Ola — der hver bærer sine egne data og sin egen oppførsel samlet på ett sted.
Forholdet mellom klasse og objekt er som mellom en kakeform og kakene. Formen finnes i ett eksemplar og beskriver fasongen; kakene lager du så mange du vil av, og hver er en egen ting du kan ta på. På fagspråket sier vi at hvert objekt er en instans av klassen.
Å skrive en klasse
La oss skrive kontoen. I Python lager du en klasse med nøkkelordet class, og navnet skrives med stor forbokstav etter konvensjon:
Her er det flere nye ting på en gang, så vi tar dem etter tur. Det første du møter er en metode med det spesielle navnet __init__. init er en konstruktør: den kjøres automatisk i det øyeblikket du lager et nytt objekt, og dens jobb er å gi objektet startverdiene sine. Du kaller den aldri direkte; den kalles for deg.
Det andre er ordet self, som dukker opp som første parameter i hver metode. self er en referanse til akkurat dette objektet — kaken vi nå holder i hånden, ikke formen og ikke noen annen kake. Når vi skriver self.eier = eier, sier vi «lagre den innsendte eieren som denne kontoens eier». Du sender aldri inn self selv; Python fyller den inn automatisk med objektet du jobber på.
Attributter — objektets data
De dataene et objekt bærer på, kaller vi attributter. I Konto er eier og saldo attributter. De settes i __init__ med self.eier = ... og lever videre så lenge objektet lever. Hvert objekt har sitt eget sett: Karis konto og Olas konto har hver sin saldo, helt uavhengig av hverandre.
FIGObjektet kari som instans av klassen Konto
Figuren over viser en konkret instans kari ved siden av klassen Konto den er laget fra. Objektboksen til venstre holder de faktiske verdiene — eier = "Kari", saldo = 1000 — mens klasseboksen til høyre beskriver oppskriften: hvilke metoder finnes, og hvilke parametre de tar. Pilen «instans av» minner om at objektet ble støpt ut fra klassen.
Metoder — objektets oppførsel
En metode er en funksjon som hører til en klasse og opererer på objektets egne data. sett_inn er en metode: den tar et beløp og øker self.saldo. Forskjellen fra en vanlig funksjon er nettopp self — metoden har tilgang til objektets attributter gjennom den.
Nå kan vi ta klassen i bruk. Å lage et objekt ser ut som et funksjonskall på selve klassenavnet:
python
kari = Konto("Kari", 1000)
kari.sett_inn(500)
print(kari.saldo) # 1500
Den første linjen lager en ny konto. Legg merke til at vi sender inn "Kari" og 1000, men ikke self — Python lager objektet, sender det inn som self til __init__, og gir oss det ferdige objektet tilbake i kari. Den andre linjen kaller metoden sett_inn på nettopp dette objektet; her blir kari automatisk self inne i metoden, og 500 blir belop.
FIGMetodekallet kari.sett_inn(500) paa stacken
Det er verdt å stoppe ved det siste. Når du skriver kari.sett_inn(500), oversetter Python det i praksis til Konto.sett_inn(kari, 500): objektet foran punktumet blir det første argumentet, self.
FIGMetodekallet kari.sett_inn(500) paa stacken
viser denne overgangen som en ramme på kall-stacken, der self peker på kari og belop er 500. Det er hele hemmeligheten bak self: det er bare objektet du kalte metoden på, sendt inn på en pen måte.
klasse
En klasse med konstruktør og metoder
python
1classKonto:2def__init__(self, eier, saldo):3self.eier = eier4self.saldo = saldo56defsett_inn(self, belop):7self.saldo = self.saldo + belop89defta_ut(self, belop):10ifbelop > self.saldo:11print("Ikke nok penger paa konto")12else:13self.saldo = self.saldo - belop141516# Lag to uavhengige objekter fra samme klasse17kari = Konto("Kari", 1000)18ola = Konto("Ola", 50)1920kari.sett_inn(500) # kari.saldo blir 150021ola.ta_ut(200) # skriver "Ikke nok penger paa konto"22print(kari.saldo) # 150023print(ola.saldo) # 50, uendret
init setter startverdiene via self. Hver av kari og ola har sin egen saldo. Metoden ta_ut vokter dataene: den nekter uttak som overstiger saldoen, så objektet aldri havner i en ulovlig tilstand.
Snutten over er en litt fyldigere versjon av kontoen, med både innskudd og uttak, og med en enkel sjekk på at du ikke kan ta ut mer enn du har. Den viser et vanlig mønster: metodene vokter objektets data og passer på at det aldri havner i en ulovlig tilstand. Det er en av de store gevinstene ved å samle data og oppførsel — reglene for hva som er lov, bor på samme sted som dataene de gjelder.
Mange objekter fra én klasse
Den virkelige styrken kommer frem når du lager mange objekter. Hver gang du kaller Konto(...), får du en helt ny, uavhengig konto med sitt eget minne:
python
kontoer = [Konto("Kari", 1000), Konto("Ola", 50), Konto("Per", 300)]
forkontoinkontoer:
konto.sett_inn(100) # alle får 100 hverforkontoinkontoer:
print(konto.eier, konto.saldo)
Her ligger tre Konto-objekter i en liste, og løkken kaller sett_inn(100) på hvert av dem etter tur. Fordi hvert objekt bærer sin egen saldo, ender Kari på 1100, Ola på 150 og Per på 400 — de påvirker ikke hverandre. Dette er mønsteret du møter overalt i større programmer: en samling objekter av samme type, behandlet i en løkke, der hvert holder rede på sin egen tilstand. Forestill deg alternativet med løse variabler kari_saldo, ola_saldo, per_saldo og en parallell liste med navn du må holde i takt — det blir uoversiktlig lenge før du når ti kontoer.
Tre feil som går igjen
Tre snublesteiner rammer nesten alle som er nye med klasser. Den første er å glemme self foran et attributt: skriver du saldo = saldo i stedet for self.saldo = saldo inne i __init__, lager du bare en lokal variabel som forsvinner når metoden er ferdig — attributtet blir aldri satt. Den andre er å glemme self som parameter i metode-definisjonen; uten den klager Python når du prøver å kalle metoden. Den tredje er å skrive __init__ med bare ett understrek, eller med stavefeil — da kjøres den aldri automatisk, og objektene dine mangler attributtene sine. Når en klasse «ikke virker», er det nesten alltid en av disse tre.
Hvorfor bry seg
Det er lett å føle at klasser bare er en mer omstendelig måte å gjøre det samme på. For de minste programmene stemmer det nesten. Men så snart et program vokser, gir objektorientering tre konkrete fordeler. Dataene henger sammen: en konto er én ting, ikke to løse variabler du må huske å holde i takt. Oppførselen bor der dataene bor: vil du vite hva en konto kan gjøre, leser du klassen. Og du kan lage så mange uavhengige objekter du vil fra samme oppskrift, uten å skrive koden på nytt for hver.
Et siste ord om navnekonvensjoner, fordi de forvirrer mange i starten. Klassenavn skrives med stor forbokstav (Konto, Bil, Student). Metoder og attributter skrives med liten (sett_inn, saldo). Og de doble understrekene i __init__ er ikke valgfrie — det er et fast, spesielt navn Python kjenner igjen og kaller automatisk. Holder du disse fra hverandre, leser koden din nesten som vanlig norsk: «lag en ny Konto, sett inn på den, skriv ut saldoen».
Mine notater
Skriv egne notater for dette kapittelet. De lagres automatisk og dukker opp i «Mine notater»-oversikten paa fag-siden.