Tilbake
1.3
Innkapsling og tilgangskontroll

1.3 Innkapsling og tilgangskontroll

Private og offentlige attributter, getters og setters.

60 min
6 oppgaver
InnkapslingTilgangskontrollGettersSetters
Du leser den tradisjonelle versjonen
Din fremgang i kapitlet
0 / 6 oppgaver

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.

Innkapsling (Encapsulation)
Innkapsling tyder å pakke data (attributtar) og funksjonar (metodar) saman i ein klasse, og samtidig skjule den interne implementasjonen frå omverda.

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 saldo

Ved å la alle aksessere saldo direkte, mistar vi kontrollen. Løysinga er innkapsling – vi gjer saldo privat.

Private vs. Public i Python

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.

✏️Privat attributt med getter og setter

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 1300

No 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 negativ

Dette er Python-måten: Enkel syntaks utad, men med kontroll og validering inni.

✏️Property med berekna verdi

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 Nordmann

Vi 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 versjoner

Den 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)  # 5

Dette 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


BegrepForklaring
InnkapslingSamle data og metodar, skjule intern implementasjon
PropertyPythonisk 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.