Oliot attribuuttina
Aikaisemmin nähtiin esimerkkejä luokista, joissa attribuutteina oli käytetty esimerkiksi listoja. Samalla tavalla myös omista luokista luotuja olioita voi käyttää toisten olioiden attribuutteina. Seuraavissa esimerkeissä on määritelty luokat Kurssi, Opiskelija ja Opintosuoritus. Opintosuorituksessa hyödynnetään kahta ensimmäistä luokkaa. Luokkien sisäinen toteutus on lyhyt, jotta esimerkki toisi esille oleellisen.
Esimerkissä jokainen luokka on kirjoitettu omaan tiedostoonsa.
Esitellään aluksi luokka Kurssi, joka on määritelty tiedostossa kurssi.py:
class Kurssi:
    def __init__(self, nimi: str, koodi: str, opintopisteet: int):
        self.nimi = nimi
        self.koodi = koodi
        self.opintopisteet = opintopisteetLuokka Opiskelija mallintaa yhtä opiskelijaa. Luokka on määritelty tiedostossa opiskelija.py:
class Opiskelija:
    def __init__(self, nimi: str, opiskelijanumero: str, opintopisteet: int):
        self.nimi = nimi
        self.opiskelijanumero = opiskelijanumero
        self.opintopisteet = opintopisteetLuokka Opintosuoritus hyödyntää luokkia Kurssi ja Opiskelija suorituksen tallentamiseen. Huomaa, että luokat tuodaan mukaan import-lauseella:
from kurssi import Kurssi
from opiskelija import Opiskelija
class Opintosuoritus:
    def __init__(self, opiskelija: Opiskelija, kurssi: Kurssi, arvosana: int):
        self.opiskelija = opiskelija
        self.kurssi = kurssi
        self.arvosana = arvosanaEsimerkki opintosuoritusten lisäämisestä listaan:
from opintosuoritus import Opintosuoritus
from kurssi import Kurssi
from opiskelija import Opiskelija
# Luodaan lista opiskelijoista
opiskelijat = []
opiskelijat.append(Opiskelija("Olli", "1234", 10))
opiskelijat.append(Opiskelija("Pekka", "3210", 23))
opiskelijat.append(Opiskelija("Leena", "9999", 43))
opiskelijat.append(Opiskelija("Tiina", "3333", 8))
# Kurssi Ohjelmoinnin perusteet
ohpe = Kurssi("Ohjelmoinnin perusteet", "ohpe1", 5)
# Annetaan suoritukset kaikille opiskelijoille, kaikille arvosanaksi 3
suoritukset = []
for opiskelija in opiskelijat:
    suoritukset.append(Opintosuoritus(opiskelija, ohpe, 3))
# Tulostetaan kaikista suorituksista opiskelijan nimi
for suoritus in suoritukset:
    print(suoritus.opiskelija.nimi)Olli Pekka Leena Tiina
Tarkastellaan lähemmin riviä print(suoritus.opiskelija.nimi):
- suorituson luokan- Opintosuoritusmukainen olio
- Niinpä muuttuja opiskelijaviittaa suoritukseen tallennettuunOpiskelija-olioon
- Opiskelija-luokan muuttuja- nimisisältää opiskelijan nimen
Milloin import tarvitaan?
Edellisessä esimerkissä käytetään muutamassa kohdassa import:ia:
from opintosuoritus import Opintosuoritus
from kurssi import Kurssi
from opiskelija import Opiskelija
# koodiImportia tarvitaan vain jos tiedostossa käytetään jossain muualla  määriteltyä koodia. Näin on esimerkiksi kun käytetään jotain Pythonin valmista kalustoa, esim. matemaattisia operaatiota tarjoavaa moduulia math:
import math
x = 10
print(f"luvun {x} {neliöjuuri math.sqrt(x)}")Edellisessä tehtävässä oletettiin, että luokat on määritelty omissa tiedostoissaan. Esimerkki toteaa mm. Esitellään aluksi luokka Kurssi, joka on määritelty tiedostossa kurssi.py ja importin tarve siis johtuu tästä.
Jos kaikki koodi sijoitetaan samaan tiedostoon, kuten kaikissa kurssin ohjelmointitehtävissä ohjeistetaan, et tarvitse import:ia luomiesi luokkien käytöön.
Jos siis päädyt kirjottamaan kurssilla seuraavanlaista koodia
from henkilo import Henkilo
# koodiratkaisusi on todennäköisesti väärä! Lisää importin käytöstä osan 7 materiaalissa.
Olion attribuuttina lista olioita
Äskeisissä esimerkeissä oliolla oli attribuuttina yksittäinen toisen luokan olio, esim. henkilöllä attribuuttina lemmikki ja opintosuorituksella attribuuttina kurssi.
Olio-ohjelmoinnissa törmätään kutenkin usein tilanteeseen, jossa oliolla on attribuuttina joukko toisen luokan oliota. Eräs tälläinen tilanne kuvaa joukkueen ja sen pelaajien välistä yhteyttä:
class Pelaaja:
    def __init__(self, nimi: str, maalit: int):
        self.nimi = nimi
        self.maalit = maalit
    def __str__(self):
        return f"{self.nimi} (maaleja {self.maalit})"
class Joukkue:
    def __init__(self, nimi: str):
        self.nimi = nimi
        self.pelaajat = []
    def lisaa_pelaaja(self, pelaaja: Pelaaja):
        self.pelaajat.append(pelaaja)
    def yhteenveto(self):
        maalit = []
        for pelaaja in self.pelaajat:
            maalit.append(pelaaja.maalit)
        print("Joukkue", self.nimi)
        print("Pelaajia", len(self.pelaajat))
        print("Pelaajien maalimäärät", maalit)Käyttöesimerkki:
kupa = Joukkue("Kumpulan pallo")
kupa.lisaa_pelaaja(Pelaaja("Erkki", 10))
kupa.lisaa_pelaaja(Pelaaja("Emilia", 22))
kupa.lisaa_pelaaja(Pelaaja("Antti", 1))
kupa.yhteenveto()Joukkue Kumpulan pallo Pelaajia 3 Pelaajien maalimäärät [10, 22, 1]
None eli viite ei mihinkään
Pythonissa muuttujat viittaavat aina johonkin olioon. On kuitenkin tilanteita, joissa haluaisimme määrittää arvon, joka ei viittaa mihinkään. Arvoa None käytetään esittämään tyhjää viittausta.
Jos esimerkiksi luokkaan joukkue lisättäisiin metodi, joka etsii joukkueen pelaajan, saattaisi olla luontevaa esittää paluuarvolla None tilanne, jossa pelaajaa ei löydy:
class Pelaaja:
    def __init__(self, nimi: str, maalit: int):
        self.nimi = nimi
        self.maalit = maalit
    def __str__(self):
        return f"{self.nimi} (maaleja {self.maalit})"
class Joukkue:
    def __init__(self, nimi: str):
        self.nimi = nimi
        self.pelaajat = []
    def lisaa_pelaaja(self, pelaaja: Pelaaja):
        self.pelaajat.append(pelaaja)
    def etsi(self, nimi: str):
        for pelaaja in self.pelaajat:
            if pelaaja.nimi == nimi:
                return pelaaja
        return NoneKäyttöesimerkki:
kupa = Joukkue("Kumpulan pallo")
kupa.lisaa_pelaaja(Pelaaja("Erkki", 10))
kupa.lisaa_pelaaja(Pelaaja("Emilia", 22))
kupa.lisaa_pelaaja(Pelaaja("Antti", 1))
pelaaja1 = kupa.etsi("Antti")
print(pelaaja1)
pelaaja2 = kupa.etsi("Jukkis")
print(pelaaja2)Antti (maaleja 1) None
None-arvojen kanssa pitää olla tarkkana. On hyvin tyypillistä, että ohjelmassa kutsutaan jotain metodia oliolle (tai pyydetään attribuutin arvoa oliolta), joka onkin None:
kupa = Joukkue("Kumpulan pallo")
kupa.lisaa_pelaaja(Pelaaja("Erkki", 10))
pelaaja = kupa.etsi("Jukkis")
print(f"Jukkiksen maalimäärä {pelaaja.maalit}")Jos näin tehdään, ohjelma päättyy virheeseen:
None-arvojen varalta onkin syytä tehdä tarkistus, ennen kuin riskialtista koodia kutsutaan:
kupa = Joukkue("Kumpulan pallo")
kupa.lisaa_pelaaja(Pelaaja("Erkki", 10))
pelaaja = kupa.etsi("Jukkis")
if pelaaja is not None:
    print(f"Jukkiksen maalimäärä {p.maalit}")
else:
    print(f"Jukkis ei pelaa Kumpulan pallossa :(")Jukkis ei pelaa Kumpulan pallossa :(