Private og offentlige attributter, getters og setters.
Innkapsling og tilgangskontroll
I førre kapittel lærte du å lage klassar med attributtar og metodar. Men det var eitt problem: Alle attributtar var direkte tilgjengelege utanfrå. Nokon kan skrive konto.saldo = -1000000 og øydeleggje logikken i programmet!
Innkapsling er prinsippet om å skjule intern data og berre eksponere det som er naudsynt. Dette er ein av dei fire grunnpilarane i objektorientert programmering, saman med arv, polymorfisme og abstraksjon.
I dette kapittelet skal du lære korleis du vernar data i klassane dine, og korleis du kontrollerer tilgangen til dei.
Føremål med innkapsling:
- Verne data: Hindre ugyldig eller uforsiktig tilgang
- Kontrollere endringar: Validere data før dei vert lagra
- Skjule kompleksitet: Brukaren treng ikkje å vite korleis ting fungerer internt
- Enklare vedlikehald: Du kan endre intern logikk utan å påverke ekstern kode
Problemet med offentlege attributtar
Lat oss sjå på eit problem med den enkle Bankkonto-klassen:
class Bankkonto:
def __init__(self, kontonummer, saldo=0):
self.kontonummer = kontonummer
self.saldo = saldo
def ta_ut(self, belop):
if belop <= self.saldo:
self.saldo -= belop
return True
return False
# Lag en konto
konto = Bankkonto("12345678", 1000)
# PROBLEM: Noen kan endre saldo direkte!
konto.saldo = -5000 # Dette burde ikke være mulig!
print(konto.saldo) # -5000
# Metoden ta_ut() er nå meningsløs
konto.ta_ut(100) # Fungerer selv med negativ saldoVed å la alle aksessere saldo direkte, mistar vi kontrollen. Løysinga er innkapsling – vi gjer saldo privat.
I Python markerer vi private attributtar med éin eller to understrekar framfor namnet:
- Offentleg (public): self.navn – kan aksesserast kvar som helst
- Verna (protected): self._navn – éin understrek, konvensjon for "intern bruk"
- Privat (private): self.__navn – to understrekar, name mangling gjer det vanskeleg å aksessere
Viktig: Python har ikkje ekte private attributtar som i Java/C++. Éin understrek er berre ein konvensjon som seier "ikkje rør dette". To understrekar gjer name mangling, men det er framleis mogleg å aksessere attributtet dersom ein verkeleg vil.
Lat oss lage ein betre Bankkonto-klasse med privat saldo:
class Bankkonto:
def __init__(self, kontonummer, saldo=0):
self.kontonummer = kontonummer
self.__saldo = saldo # Privat attributt (to understreker)
def hent_saldo(self):
"""Getter: Hent saldo (read-only)"""
return self.__saldo
def sett_inn(self, belop):
"""Setter: Legg inn penger (med validering)"""
if belop > 0:
self.__saldo += belop
return True
return False
def ta_ut(self, belop):
"""Setter: Ta ut penger (med validering)"""
if 0 < belop <= self.__saldo:
self.__saldo -= belop
return True
return False
# Bruk
konto = Bankkonto("12345678", 1000)
# Vi kan hente saldo
print(konto.hent_saldo()) # 1000
# Men vi kan ikke endre den direkte
# konto.__saldo = -5000 # Dette fungerer IKKE som forventet
# Vi må bruke metodene
konto.sett_inn(500)
konto.ta_ut(200)
print(konto.hent_saldo()) # 1300
# Forsøk på ugyldig operasjon
konto.sett_inn(-100) # Returnerer False, ingen endring
print(konto.hent_saldo()) # Fortsatt 1300No er saldo verna! Vi må bruke metodane for å endre han, og metodane validerer input.
Properties: Pythonisk tilgangskontroll
Å skrive konto.hent_saldo() kvar gong vi vil lese saldo er litt tungvint. Python har ei elegant løysing: properties.
Ei property gjer at ein metode oppfører seg som eit attributt. Vi bruker dekoratoren @property for getter, og @attributt.setter for setter:
class Bankkonto:
def __init__(self, kontonummer, saldo=0):
self.kontonummer = kontonummer
self.__saldo = saldo
@property
def saldo(self):
"""Getter for saldo"""
return self.__saldo
@saldo.setter
def saldo(self, verdi):
"""Setter for saldo (med validering)"""
if verdi >= 0:
self.__saldo = verdi
else:
raise ValueError("Saldo kan ikke være negativ")
# Bruk ser ut som vanlige attributter!
konto = Bankkonto("12345678", 1000)
print(konto.saldo) # 1000 (kaller getter)
konto.saldo = 1500 # Kaller setter (med validering)
print(konto.saldo) # 1500
konto.saldo = -100 # ValueError: Saldo kan ikke være negativDette er Python-måten: Enkel syntaks utad, men med kontroll og validering inni.
Properties er òg nyttige for berekna verdiar som ikkje vert lagra, men vert rekna ut on-the-fly:
class Person:
def __init__(self, fornavn, etternavn, fødselsår):
self.fornavn = fornavn
self.etternavn = etternavn
self.fødselsår = fødselsår
@property
def fullt_navn(self):
"""Beregnet property: Kombinerer fornavn og etternavn"""
return f"{self.fornavn} {self.etternavn}"
@property
def alder(self):
"""Beregnet property: Regner ut alder basert på fødselsår"""
from datetime import datetime
return datetime.now().year - self.fødselsår
# Bruk
person = Person("Kari", "Nordmann", 1995)
print(person.fullt_navn) # Kari Nordmann
print(person.alder) # 31 (i 2026)
# Fullt navn oppdateres automatisk hvis vi endrer fornavn
person.fornavn = "Anne"
print(person.fullt_navn) # Anne NordmannVi treng ikkje lagre fullt_navn eller alder som eigne attributtar – dei vert berekna når vi treng dei.
Information Hiding – kvifor gøyme data?
Information hiding (informasjonsskjuling) er eit nøkkelomgrep i OOP. Det handlar ikkje om å vere hemmelegheitsfull, men om å:
1. Redusere kompleksitet
Brukaren treng ikkje å vite korleis saldo vert lagra internt – dei bruker berre metodane.
2. Hindre feil
Dersom saldo var offentleg, kunne nokon ved eit uhell setje han til ein ugyldig verdi.
3. Gjere endringar mogleg
Du kan endre korleis saldo vert lagra (t.d. i øre i staden for kroner) utan at det påverkar kode som bruker klassen.
4. Tvinge bruk av korrekt API
Ved å gjere attributtar private, tvingar du brukarar til å bruke dine validerte metodar.
Eksempel på intern endring utan å påverke brukarar:
# Versjon 1: Saldo lagres i kroner
class Bankkonto:
def __init__(self, saldo=0):
self.__saldo_kr = saldo
@property
def saldo(self):
return self.__saldo_kr
# Versjon 2: Saldo lagres i øre (for bedre presisjon)
class Bankkonto:
def __init__(self, saldo=0):
self.__saldo_øre = saldo * 100 # Lagre i øre
@property
def saldo(self):
return self.__saldo_øre / 100 # Returner i kroner
# Brukeren merker ingen forskjell!
konto = Bankkonto(1000)
print(konto.saldo) # 1000 i begge versjonerDen interne representasjonen endra seg, men API-et (det brukaren ser) er det same.
Read-only properties
Nokre gonger vil du at eit attributt skal vere read-only (skrivevernt) – det kan lesast, men ikkje endrast utanfrå. Dette vert gjort ved å lage ein getter utan setter:
class Produkt:
def __init__(self, navn, pris):
self.__navn = navn
self.__pris = pris
self.__antall_solgt = 0
@property
def navn(self):
"""Read-only: Navn kan ikke endres etter opprettelse"""
return self.__navn
@property
def pris(self):
return self.__pris
@pris.setter
def pris(self, ny_pris):
"""Pris kan endres (men valideres)"""
if ny_pris > 0:
self.__pris = ny_pris
else:
raise ValueError("Pris må være positiv")
@property
def antall_solgt(self):
"""Read-only: Kan bare endres gjennom selg()-metoden"""
return self.__antall_solgt
def selg(self, antall=1):
self.__antall_solgt += antall
# Bruk
produkt = Produkt("Laptop", 10000)
# Kan lese alle
print(produkt.navn) # Laptop
print(produkt.pris) # 10000
print(produkt.antall_solgt) # 0
# Kan endre pris
produkt.pris = 9500
print(produkt.pris) # 9500
# Kan IKKE endre navn eller antall_solgt direkte
# produkt.navn = "PC" # AttributeError: can't set attribute
# produkt.antall_solgt = 100 # AttributeError: can't set attribute
# Men kan selge via metode
produkt.selg(5)
print(produkt.antall_solgt) # 5Dette gir presis kontroll over kva som kan endrast og korleis.
Når skal du bruke innkapsling?
Ikkje alle klassar treng streng innkapsling. Her er nokre retningslinjer:
Bruk private attributtar når:
- Attributtet må validerast (t.d. saldo, alder, epost)
- Attributtet vert brukt i utrekningar som må vere konsistente
- Du vil reservere retten til å endre intern representasjon seinare
- Attributtet er kritisk for integriteten til objektet
Public attributtar er OK når:
- Det er enkle data utan logikk (t.d. koordinatar: x, y)
- Klassen er ein enkel datastruktur (dataclass)
- Det er intern kode som ikkje skal brukast av andre
Ein god tommelfingerregel:
Start med public attributtar for å gjere det enkelt. Dersom du seinare merkar at du treng validering eller kontroll, gjer dei private og legg til properties.
Oppsummering
I dette kapittelet har du lært:
- Innkapsling: samlar data og metodar og skjuler intern implementasjon.
- Private vs. public: understrek markerer private attributtar i Python.
- Getter og setter: kontrollert tilgang til attributtar.
- Properties: pythonisk måte å styre tilgang på.
- Information hiding: skjuler interne detaljar frå brukaren.
Noekkelbegreper
| Begrep | Forklaring |
|---|---|
| Innkapsling | Samle data og metodar, skjule intern implementasjon |
| Property | Pythonisk tilgangskontroll for attributtar |
| Information hiding | Å skjule interne detaljar |
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.