Private og offentlige attributter, getters og setters.
Innkapsling og tilgangskontroll
I forrige kapittel lærte du å lage klasser med attributter og metoder. Men det var ett problem: Alle attributter var direkte tilgjengelige utenfra. Noen kan skrive konto.saldo = -1000000 og ødelegge logikken i programmet!
Innkapsling er prinsippet om å skjule intern data og bare eksponere det som er nødvendig. Dette er en av de fire grunnpilarene i objektorientert programmering, sammen med arv, polymorfisme og abstraksjon.
I dette kapittelet skal du lære hvordan du beskytter data i klassene dine, og hvordan du kontrollerer tilgangen til dem.
Formål med innkapsling:
- Beskytte data: Hindre ugyldig eller uforsiktig tilgang
- Kontrollere endringer: Validere data før de lagres
- Skjule kompleksitet: Brukeren trenger ikke å vite hvordan ting fungerer internt
- Enklere vedlikehold: Du kan endre intern logikk uten å påvirke ekstern kode
Problemet med offentlige attributter
La oss se på et 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, mister vi kontrollen. Løsningen er innkapsling – vi gjør saldo privat.
I Python markerer vi private attributter med én eller to understreker foran navnet:
- Offentlig (public): self.navn – kan aksesseres hvor som helst
- Beskyttet (protected): self._navn – én understrek, konvensjon for "intern bruk"
- Privat (private): self.__navn – to understreker, name mangling gjør det vanskelig å aksessere
Viktig: Python har ikke ekte private attributter som i Java/C++. En understrek er bare en konvensjon som sier "ikke rør dette". To understreker gjør name mangling, men det er fortsatt mulig å aksessere attributtet hvis man virkelig vil.
La oss lage en bedre 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 1300Nå er saldo beskyttet! Vi må bruke metodene for å endre den, og metodene validerer input.
Properties: Pythonisk tilgangskontroll
Å skrive konto.hent_saldo() hver gang vi vil lese saldo er litt tungvint. Python har en elegant løsning: properties.
En property gjør at en metode oppfører seg som et 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 også nyttige for beregnet verdier som ikke lagres, men regnes 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 trenger ikke lagre fullt_navn eller alder som egne attributter – de beregnes når vi trenger dem.
Information Hiding – hvorfor gjemme data?
Information hiding (informasjonsskjuling) er et nøkkelbegrep i OOP. Det handler ikke om å være hemmelighetsfull, men om å:
1. Redusere kompleksitet
Brukeren trenger ikke å vite hvordan saldo lagres internt – de bruker bare metodene.
2. Forhindre feil
Hvis saldo var offentlig, kunne noen ved et uhell sette den til en ugyldig verdi.
3. Muliggjøre endringer
Du kan endre hvordan saldo lagres (f.eks. i øre i stedet for kroner) uten at det påvirker kode som bruker klassen.
4. Tvinge bruk av korrekt API
Ved å gjøre attributter private, tvinger du brukere til å bruke dine validerte metoder.
Eksempel på intern endring uten å påvirke brukere:
# 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 endret seg, men API-et (det brukeren ser) er det samme.
Read-only properties
Noen ganger vil du at et attributt skal være read-only (skrivebeskyttet) – det kan leses, men ikke endres utenfra. Dette gjøres ved å lage en getter uten 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 hva som kan endres og hvordan.
Når skal du bruke innkapsling?
Ikke alle klasser trenger streng innkapsling. Her er noen retningslinjer:
Bruk private attributter når:
- Attributtet må valideres (f.eks. saldo, alder, epost)
- Attributtet brukes i beregninger som må være konsistente
- Du vil reservere retten til å endre intern representasjon senere
- Attributtet er kritisk for objektets integritet
Public attributter er OK når:
- Det er enkle data uten logikk (f.eks. koordinater: x, y)
- Klassen er en enkel datastruktur (dataclass)
- Det er intern kode som ikke skal brukes av andre
En god tommelfingerregel:
Start med public attributter for enkelhetens skyld. Hvis du senere merker at du trenger validering eller kontroll, gjør dem private og legg til properties.
Oppsummering
I dette kapittelet har du lært:
- Innkapsling: samler data og metoder og skjuler intern implementasjon.
- Private vs. public: understrek markerer private attributter i Python.
- Getter og setter: kontrollert tilgang til attributter.
- Properties: pythonisk måte å styre tilgang på.
- Information hiding: skjuler interne detaljer fra brukeren.
Noekkelbegreper
| Begrep | Forklaring |
|---|---|
| Innkapsling | Samle data og metoder, skjule intern implementasjon |
| Property | Pythonisk tilgangskontroll for attributter |
| Information hiding | Å skjule interne detaljer |
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.