Osa 8

Egna klasser

En klass definieras med nyckelordet class. Syntaxen ser ut enligt följande:

class KlassNamn:
    # Klassdefinitionen

Klasser namnges vanligtvis med kamelnotation. Detta innebär att alla ord i klassnamnet skrivs tillsammans, utan mellanslag, och att varje ord börjar med stor bokstav. Följande exempel på klassnamn följer denna konvention:

  • Veckodag
  • Bankkonto
  • BiblioteksDatabas
  • PythonKursBetyg

En enskild klassdefinition bör representera en enskild helhet, vars innehåll bör vara sammanlänkat på något sätt. I mer komplicerade program kan klasser innehålla medlemmar av andra klasser. Till exempel kan klassen Kurs innehålla objekt av klasserna Lektion, ÖvningsTillfälle osv.

Låt oss ta en titt på strukturen för en klassdefinition.

class Bankkonto:
    pass

Koden ovan talar om för Python att vi här definierar en klass med namnet Bankkonto. Klassen innehåller ingen funktionalitet ännu, men vi kan redan skapa ett objekt baserat på klassen.

Låt oss titta på ett program där två variabler läggs till ett Bankkonto-objekt: saldo och ägare. Alla variabler som är kopplade till ett objekt kallas dess attribut, eller mer specifikt, dataattribut, eller ibland instansvariabler.

De attribut som är kopplade till ett objekt kan nås via objektet:

class Bankkonto:
    pass

peters_konto = Bankkonto()
peters_konto.agare = "Peter Python"
peters_konto.saldo = 5.0

print(peters_konto.agare)
print(peters_konto.saldo)
Exempelutskrift

Peter Python 5.0

Dataattributen är endast tillgängliga via det objekt som de är kopplade till. Varje Bankkonto-objekt som skapas baserat på Bankkonto-klassen har sina egna värden kopplade till dataattributen. Dessa värden kan nås genom att hänvisa till objektet i fråga:

konto = Bankkonto()
konto.saldo = 155.50

print(konto.saldo) # Detta refererar till dataattributen saldo, som är kopplad till kontot
print(saldo) # DETTA ORSAKAR ETT FEL, eftersom det inte finns någon sådan oberoende variabel, och objektreferensen saknas

Att lägga till en konstruktor

I exemplet ovan såg vi att en ny instans av en klass kan skapas genom att anropa klassens konstruktormetod på följande sätt: KlassensNamn(). Ovan kopplade vi sedan dataattribut till objektet separat, men det är ofta bekvämare att skicka dessa initiala värden för attribut direkt när objektet skapas. I exemplet ovan hade vi först ett Bankkonto-objekt utan dessa attribut, och attributen existerade först efter att de uttryckligen hade deklarerats.

Att deklarera attribut utanför konstruktorn leder till en situation där olika instanser av samma klass kan ha olika attribut. Följande kod ger ett fel eftersom vi nu har ett annat Bankkonto-objekt, pernillas_konto, som inte innehåller samma attribut:

class Bankkonto:
    pass

peters_konto = Bankkonto()
peters_konto.agare = "Peter"
peters_konto.saldo = 1400

pernillas_konto = Bankkonto()
pernillas_konto.agare = "Pernilla"

print(peters_konto.saldo)
print(pernillas_konto.saldo) # DETTA ORSAKAR ETT FEL

Istället för att deklarera attribut efter att varje instans av klassen har skapats, är det därför oftast en bättre idé att initialisera attributens värden när konstruktorn anropas. Eftersom klassdefinitionen Bankkonto för närvarande bara är en ram, antas konstruktormetoden implicit av Python-tolkaren, men det är möjligt att definiera egna konstruktormetoder, och det är precis vad vi kommer att göra nu.

En konstruktormetod är en metoddeklaration med det speciella namnet __init__, som vanligtvis inkluderas i början av en klassdefinition.

Låt oss ta en titt på en Bankkonto-klass där vi nu även definierat en konstruktor:

class Bankkonto:

    # Konstruktorn
    def __init__(self, saldo: float, agare: str):
        self.saldo = saldo
        self.agare = agare

Namnet på konstruktorn är alltid __init__. Lägg märke till de två understrecken på båda sidorna av ordet init.

Den första parametern i en konstruktorsdefinition heter alltid self. Detta refererar till själva objektet och är nödvändigt för att deklarera alla attribut som är knutna till objektet. Tilldelningen

self.saldo = saldo

tilldelar objektets saldoattribut det sald som tagits emot som argument. Det är vanligt att använda samma variabelnamn för parametrarna och dataattributen som definieras i en konstruktor, men variabelnamnen self.saldo och saldo ovan hänvisar till två olika variabler:

  • Variabeln self.saldo är ett attribut för objektet. Varje Bankkonto-objekt har sitt eget saldo.

  • Variabeln saldo är en parameter i konstruktorsmetoden __init__. Dess värde sätts till det värde som skickas som argument när konstruktorn anropas (dvs. när en ny instans av klassen skapas).

När vi har definierat parametrarna för konstruktorsmetoden kan vi skicka de önskade initiala värdena för dataattributen som argument när ett nytt objekt skapas:

class Bankkonto:

    # Konstruktorn
    def __init__(self, saldo: float, agare: str):
        self.saldo = saldo
        self.agare = agare

# Parametern self ges inget värde, utan Python ger ett sådant automatiskt
peters_konto = Bankkonto(100, "Peter Python")
pernillas_konto = Bankkonto(20000, "Pernilla Pythonson")

print(peters_konto.saldo)
print(pernillas_konto.saldo)
Exempelutskrift

100 20000

Det är nu mycket enklare att arbeta med Bankkonto-objekten, eftersom värdena kan skickas när objektet skapas, och de två separata instanserna kan hanteras på ett mer förutsägbart och enhetligt sätt. Att deklarera dataattribut i konstruktorn säkerställer också att attributen verkligen deklareras, och att de önskade initiala värdena alltid ges av programmeraren som använder klassen.

Det är fortfarande möjligt att ändra de initiala värdena för dataattributen senare i programmet:

class Bankkonto:

    # Konstruktorn
    def __init__(self, saldo: float, agare: str):
        self.saldo = saldo
        self.agare = agare

peters_konto = Bankkonto(100, "Peter Python")
print(peters_konto.saldo)

# Ändra saldot till 1500
peters_konto.saldo = 1500
print(peters_konto.saldo)

# Vi lägger till 2000 till saldot
peters_konto.saldo += 2000
print(peters_konto.saldo)
Exempelutskrift

100 1500 3500

Låt oss titta på ett annat exempel på klasser och objekt. Vi ska skriva en klass som modellerar lotteridragning:

from datetime import date

class LotteriDragning:

    def __init__(self, vecka: int, datum: date, nummer: list):
        self.vecka = vecka
        self.datum = datum
        self.nummer = nummer


# Vi skapar ett nytt LotteriDragning-objekt
runda1 = LotteriDragning(1, date(2021, 1, 2), [1,4,8,12,13,14,33])

# Vi skriver ut resultatet
print(runda1.vecka)
print(runda1.datum)

for nummer in runda1.nummer:
    print(nummer)
Exempelutskrift

1 2021-01-02 1 4 8 12 13 14 33

Som du kan se ovan kan attributen vara av vilken typ som helst. Här har varje LotteriDragning-objekt attribut av typerna list och date.

Loading
Loading

Användning av objekt från egengjorda klasser

Objekt som bildas från dina egna klassdefinitioner skiljer sig inte från andra Python-objekt. De kan skickas som argument och returnera värden precis som alla andra objekt. Vi kan till exempel skriva hjälpfunktioner för att arbeta med bankkonton:

# funktionen skapar ett nytt bankkonto-objekt och returnerar det
def oppna_konto(namn: str):
    nytt_konto =  Bankkonto(0, namn)
    return nytt_konto

# denna funktion lägger till det belopp som anges som argument till saldot som anges som argument
def lagg_in_pengar_pa_kontot(konto: Bankkonto, summa: int):
    konto.saldo += summa

peters_konto = oppna_konto("Peter Python")
print(peters_konto.saldo)

lagg_in_pengar_pa_kontot(peters_konto, 500)

print(peters_konto.saldo)
Exempelutskrift

0 500

Loading
Loading
Loading
Du har nått slutet av den här delen! Fortsätt till nästa del:

Se dina poäng genom att klicka på cirkeln nere till höger av sidan.