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 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.

Innkapsling (Encapsulation)
Innkapsling betyr å pakke data (attributter) og funksjoner (metoder) sammen i en klasse, og samtidig skjule den interne implementasjonen fra omverdenen.

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 saldo

Ved å la alle aksessere saldo direkte, mister vi kontrollen. Løsningen er innkapsling – vi gjør saldo privat.

Private vs. Public i Python

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.

✏️Privat attributt med getter og setter

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 1300

Nå 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 negativ

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

✏️Property med beregnet verdi

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 Nordmann

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

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

Dette 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


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