Osa 8

Luokat ja oliot

Edellisessä osassa käsitellyt esimerkkioliot – listat, tuplet, sanakirjat ja merkkijonot – ovat siinä mielessä erikoistapauksia, että niiden kaikkien muodostamiseen on Pythonissa sisäänrakennettuna oma syntaksinsa:

# Lista luodaan antamalla arvot hakasuluissa
lista = [1,2,3]

# Merkkijonovakio tunnistetaan lainausmerkeistä
mjono = "Moi kaikki!"

# Sanakirja luodaan aaltosulkeilla
sanakirja = {"yksi": 1, "kaksi:": 2}

# Tuplessa arvot ovat sulkeissa
oma_tuple = (1,2,3)

Muita olioita muodostettaessa kutsutaan erityistä metodia, joka luo olion. Tällaista metodia kutsutaan konstruktoriksi. Tarkastellaan esimerkkinä murtolukuolioiden muodostamista Fraction-luokasta:

# Tuodaan käyttöön luokka Fraction modulista fractions
from fractions import Fraction

# Luodaan pari uutta murtolukuoliota
puolikas = Fraction(1,2)

kolmasosa = Fraction(1,3)

kolmas = Fraction(3,11)

# Tulostetaan
print(puolikas)
print(kolmasosa)
print(kolmas)

# Murtoluvuilla voi myös laskea
print(puolikas + kolmasosa)
Esimerkkitulostus

1/2 1/3 3/11 5/6

Esimerkistä huomataan, että konstuktorikutsut poikkeavat aiemmista metodikutsuista. Konstruktorikutsuja ei ole sidottu tiettyyn olioon (mikä on sikäli loogista, että olio muodostetaan kutsumalla konstruktoria). Lisäksi metodin nimi on kirjoitettu isolla alkukirjaimella: puolikas = Fraction(1,2). Pureudutaan tarkemmin olion muodostamisen mekanismiin esittelemällä luokan käsite.

Luokka on olion käsikirjoitus

Materiaalissa on jo aiemmin vilahtanut käsite luokka. Edellisessä esimerkissä otettiin käyttöön luokka Fraction moduulista fractions. Uudet oliot muodostettiin kutsumalla luokan Fraction konstruktoria.

Luokassa määritellään siitä muodostettavien olioiden rakenne ja toiminnallisuus. Luokkaa nimitetään tästä syystä joskus olion käsikirjoitukseksi. Luokassa siis kerrotaan, millaista tietoa olio sisältää, ja määritellään metodit, joiden avulla oliota voidaan käsitellä. Olio-ohjelmoinnilla tarkoitetaan ohjelmointitapaa, jossa ohjelman toiminnallisuus tapahtuu luokkien ja niistä muodostettujen olioiden avulla.

Yhdestä luokasta voidaan muodostaa useita olioita. Niin kuin aiemmin kerrottiin, oliot ovat itsenäisiä - muutokset olioon eivät vaikuta muihin luokasta muodostettuihin olioihin. Jokaisella oliolla on oma tietosisältönsä. Vähän yksinkertaistaen voisi sanoa, että

  • luokassa määritellään muuttujat ja
  • oliota muodostaessa niille annetaan arvot.

Luodaan esimerkkinä Fraction-luokasta olio ja tulostetaan sen osoittaja ja nimittäjä:

from fractions import Fraction

luku = Fraction(2,5)

# Tulostetaan osoittaja
print(luku.numerator)

# ...ja sitten nimittäjä
print(luku.denominator)
Esimerkkitulostus

2 5

Luokassa Fraction on siis määritelty, että olioilla on muuttujat numerator ja denominator. Jokaisella oliolla on kuitenkin oma arvonsa näille muuttujille.

Samalla tavalla date-luokasta muodostetuilla olioilla on kaikilla omat itsenäiset arvonsa vuodelle, kuukaudelle ja päivämäärälle:

from datetime import date

joulu = date(2020, 12, 24)
juhannus = date(2020, 6, 20)

# Tulostetaan kuukaudet molemmista
print(joulu.month)
print(juhannus.month)
Esimerkkitulostus

12 6

Luokassa date on siis määritelty, että luokasta muodostettavilla olioilla on muuttujat year, month ja day. Kun luokasta muodostetaan olio, annetaan muuttujille arvot. Joka oliolla on omat arvonsa muuttujille.

Olioita käsittelevät funktiot

Funktioiden parametrina oleviin olioihin ei liity oikeastaan mitään sen kummempaa. Niitä on jo kurssin aiemmissa osissa nähty runsaasti. Seuraavassa on esimerkki funktiosta, joka tarkastaa, onko sen parametrina oleva date-olio viikonloppu:

def onko_viikonloppu(paiva: date):
    viikonpaiva = paiva.isoweekday()
    return viikonpaiva == 6 or viikonpaiva == 7

Funktio siis käyttää parametrina olevan olion metodia isoweekday, joka palauttaa viikonpäivää vastaavan numeron niin, että maanantai on 1, tiistai on 2, jne.

Funktiota käytetään seuraavasti:

joulu = date(2020, 12, 24)
juhannus = date(2020, 6, 20)

print(onko_viikonloppu(joulu))
print(onko_viikonloppu(juhannus))
Esimerkkitulostus

False True

Metodi vs. olion muuttuja

Jos tarkastellaan date-oliota, niin huomataan, että sen käsittely poikkeaa hieman riippuen siitä, mitä asiaa olion sisällöstä tarkastellaan:

paiva = date(2020, 12, 24)

# kutsutaan metodia
viikonpaiva = paiva.isoweekday()

# viitataan olion muuttujaan
kuukausi = paiva.month

print("Viikonpäivä:", viikonpaiva)
print("Kuukausi:", kuukausi)
Esimerkkitulostus

Viikonpäivä: 4 Kuukausi: 12

Päiväolion viikonpäivä saadaan siis selville kutsumalla metodia isoweekday:

viikonpaiva = paiva.isoweekday()

Koska on kyse metodikutsusta, niin metodin nimen perään laitetaan sulut. Jos sulut unohtuvat, on lopputulos outo:

viikonpaiva =  paiva.isoweekday
print("Viikonpäivä:", viikonpaiva)
Esimerkkitulostus

Viikonpäivä: <built-in method isoweekday of datetime.date object at 0x10ed66450>

Päiväolioon liittyvä kuukausi taas on olion muuttuja, ja sen arvo selviää viittaamalla muuttujaan

kuukausi = paiva.month

Nyt siis käytössä ei ole sulkuja. Jos tässä tilanteessa yritettäisiin käyttää sulkuja, ohjelma aiheuttaisi virheen:

kuukausi = paiva.month()
Esimerkkitulostus
Traceback (most recent call last): File "", line 1, in TypeError: 'int' object is not callable
Loading
Loading
Seuraava osa: