Objekt som attribut
Vi har redan sett exempel på klasser som har listor som attribut. Eftersom det alltså inte finns något som hindrar oss från att inkludera mutabla objekt som attribut i våra klasser, kan vi lika gärna använda instanser av våra egna klasser som attribut i andra klasser som vi själva har definierat. I följande exempel kommer vi att definiera klasserna Kurs
, Studerande
och SlutfordKurs
. En slutförd kurs använder sig av de två första klasserna. Klassdefinitionerna är mycket korta och enkla för att vi bättre ska kunna koncentrera oss på tekniken att använda instanser av våra egna klasser som attribut.
Vi kommer att anta att varje klass definieras i en separat fil.
Först definierar vi klassen Kurs
i en fil med namnet kurs.py
:
class Kurs:
def __init__(self, namn: str, kod: str, studiepoang: int):
self.namn = namn
self.kod = kod
self.studiepoang = studiepoang
Till näst, klassen Studerande
i en fil med namnet studerande.py
:
class Studerande:
def __init__(self, namn: str, studerandenummer: str, studiepoang: int):
self.namn = namn
self.studerandenummer = studerandenummer
self.studiepoang = studiepoang
Slutligen är klassen SlutfordKurs
definierad i en fil med namned SlutfordKurs.py
. Eftersom den använder de två andra klasserna, måste de importeras innan de kan användas:
from kurs import Kurs
from studerande import Studerande
class SlutfordKurs:
def __init__(self, studerande: Studerande, kurs: Kurs, vitsord: int):
self.studerande = studerande
self.kurs = kurs
self.vitsord = vitsord
Här är ett exempel av en huvudfunktion som lägger till några slutförda kurser i en lista:
from slutfordkurs import SlutfordKurs
from kurs import Kurs
from studerande import Studerande
# Vi skapar en lista av studeranden
studeranden = []
studeranden.append(Studerande("Olle", "1234", 10))
studeranden.append(Studerande("Peter", "3210", 23))
studeranden.append(Studerande("Lena", "9999", 43))
studeranden.append(Studerande("Tina", "3333", 8))
# Kurssen Introduktion till Programmering
itp = Kurs("Introduktion till Programmering", "itp1", 5)
# Vi ger prestationer för varje student, med vitsordet 3 till alla
prestationer = []
for studerande in studeranden:
prestationer.append(SlutfordKurs(studerande, itp, 3))
# Vi skriver ut studerandenas namn för varje avklarad kurs
for prestation in prestationer:
print(prestation.studerande.namn)
Olle Peter Lena Tina
Vad exakt händer med alla prickar på raden print(kurs.studerande.namn)
?
kurs
är en instans av klassenSlutfordKurs
studerande
refererar till ett attribut i objektetSlutfordKurs
, som är ett objekt av typenStuderande
- attributnamnet i
Studerande
-objektet innehåller namnet på studenten
När är en import nödvändig?
I exemplen ovan förekommer en import
-sats ganska många gånger:
from slutfordkurs import SlutfordKurs
from kurs import Kurs
from studerande import Studerande
# kod
En importsats är bara nödvändig när man använder kod som är definierad någonstans utanför den aktuella filen (eller Python-tolksessionen). Detta inkluderar situationer där vi vill använda något som är definierat i Pythons standardbibliotek. Modulen math
innehåller till exempel vissa matematiska operationer:
import math
x = 10
print(f"kvadratroten av {x} är {math.sqrt(x)}")
I exemplet ovan antog vi att de tre klasserna definierades i var sin fil och att huvudfunktionen kördes från ytterligare en fil. Det var därför import
-satserna var nödvändiga.
Om all programkod skrivs i samma fil, vilket de flesta övningarna i den här kursen rekommenderar, behöver du inte import
-satser för att använda de klasser du har definierat.
Ifall du märker dig själv skriva något i stil med
from person import Person
# kod
är det sannolikt att du förstått nånting felaktigt. Ifall du behöver en uppfriskare så introducerades import
deklarationen för första gången i modul 7 av kursmaterialet.
En lista med objekt som attribut till ett objekt
I exemplen ovan använde vi enstaka instanser av andra klasser som attribut: en Person har ett enda Husdjur som attribut, och en SlutfordKurs har en Studerande och en Kurs som attribut.
I objektorienterad programmering är det ofta så att vi vill ha en samling objekt som attribut. Till exempel följer relationen mellan ett idrottslag och dess spelare detta mönster:
class Spelare:
def __init__(self, namn: str, mal: int):
self.namn = namn
self.mal = mal
def __str__(self):
return f"{self.namn} (mål {self.mal})"
class Lag:
def __init__(self, namn: str):
self.namn = namn
self.spelare = []
def tillsatt_spelare(self, spelare: Spelare):
self.spelare.append(spelare)
def sammanfattning(self):
mal = []
for spelare in self.spelare:
mal.append(spelare.mal)
print("Lag:", self.namn)
print("Spelare:", len(self.spelare))
print("Spelarnas målmängd:", mal)
Ett exempel på hur vår klass fungerar:
gumboll = Lag("Gumtäkts boll")
gumboll.tillsatt_spelare(Spelare("Erik", 10))
gumboll.tillsatt_spelare(Spelare("Emilia", 22))
gumboll.tillsatt_spelare(Spelare("Anton", 1))
gumboll.sammanfattning()
Lag: Gumtäkts boll Spelare: 3 Spelarnas målmängd: [10, 22, 1]
None: en referens till ingenting
I Python-programmering refererar alla initialiserade variabler till ett objekt. Det finns dock oundvikligen situationer där vi måste referera till något som inte existerar, utan att orsaka fel. Nyckelordet None
representerar just ett sådant "tomt" objekt.
Låt oss fortsätta från exemplet med lag och spelare ovan och anta att vi vill lägga till en metod för att söka efter spelare i laget med hjälp av spelarens namn. Om ingen sådan spelare hittas kan det vara vettigt att returnera None
:
class Spelare:
def __init__(self, namn: str, mal: int):
self.namn = namn
self.mal = mal
def __str__(self):
return f"{self.namn} (mål {self.mal})"
class Lag:
def __init__(self, namn: str):
self.namn = namn
self.spelare = []
def tillsatt_spelare(self, spelare: Spelare):
self.spelare.append(spelare)
def sok(self, namn: str):
for spelare in self.spelare:
if spelare.namn == namn:
return spelare
return None
Låt oss testa vår funktion:
gumboll = Lag("Gumtäkts boll")
gumboll.tillsatt_spelare(Spelare("Erik", 10))
gumboll.tillsatt_spelare(Spelare("Emilia", 22))
gumboll.tillsatt_spelare(Spelare("Anton", 1))
spelare1 = gumboll.sok("Anton")
print(spelare1)
spelare2 = gumboll.sok("Johan")
print(spelare2)
Anton (mål 1) None
Var dock försiktig med None
. Det kan ibland orsaka mer problem än det löser. Det är ett vanligt programmeringsfel att försöka komma åt en metod eller ett attribut via en referens som utvärderas till None
:
gumboll = Lag("Gumtäkts boll")
gumboll.tillsatt_spelare(Spelare("Erik", 10))
spelare = gumboll.sok("Johan")
print(f"Johans målmängd {spelare.mal}")
Exekverande av ovanstående skulle orsaka ett fel:
Det är en god idé att kontrollera om det finns None
innan du försöker komma åt några attribut eller metoder för returvärden:
gumboll = Lag("Gumtäkts boll")
gumboll.tillsatt_spelare(Spelare("Erik", 10))
spelare = gumboll.sok("Johan")
if spelare is not None:
print(f"Johans målmängd {p.mal}")
else:
print(f"Johan spelar inte i Gumtäkts boll :(")
Johan spelar inte i Gumtäkts boll :(
Se dina poäng genom att klicka på cirkeln nere till höger av sidan.