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)
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)
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)
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))
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)
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)
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()