Python-oppimateriaali (CHEM-A2600)

Lyhyt opas Python-ohjelmointiin

Olio-ohjelmointia 1

Aiemmin tällä kurssilla olemme tutustuneet erilaisiin tietorakenteisiin (listat, sanakirjat, monikot, NumPy-taulukot) ja funktioihin, joilla näitä tietorakenteita voi käsitellä (esim. max(lista)).

Tutustutaan lopuksi lyhyesti olioihin (engl. object). Voimme ajatella olioita tietorakenteina, jotka sisältävät myös tietojen käsittelyyn tarkoitettuja funktioita.

Luokan määritteleminen ja olioiden luominen

Jotta voimme luoda uuden olion, meidän pitää ensin määritellä luokka (engl. class) joka kuvaa olion ominaisuudet. Määritellään luokka Molekyyli (luokkien nimet kirjoitetaan isolla alkukirjaimella):

class Molekyyli:
    def __init__(self, kaava, moolimassa):
        self.kaava = kaava
        self.moolimassa = moolimassa
       
    def laske_ainemaara(self, massa):
        return massa / self.moolimassa

Molekyyli-luokka sisältää kaksi funktiomäärittelyä. Näitä funktioita kutsutaan metodeiksi (engl. method) erotuksena tavallisista funktioista, jotka eivät kuulu mihinkään luokkaan. 

Molekyyli-luokka sisältää käynnistysmetodin __init__ ja metodin laske_ainemaara. Huomaa, että molempien metodien ensimmäinen parametri on self. Tämä parametri viittaa aina olioon itseensä. Python hoitaa self-parametrin automaattisesti, eli sitä ei anneta, kun metodia kutsutaan.

Käynnistysmetodissa __init__ luodaan oliolle kaksi kenttää: kaava ja moolimassa. Kenttiin pitää viitata metodin self-parametrin avulla.

Katsotaan, mitä määrittelemällämme luokalla voidaan nyt tehdä. Luodaan Molekyyli-luokkaan pohjautuvat oliot metaani ja etaani:

metaani = Molekyyli("CH4", 16.04) 
etaani = Molekyyli("C2H6", 30.07)

Käsky Molekyyli("CH4", 16.04) tarkoittaa, että Molekyyli-luokan __init__-metodia kutsutaan parametreilla "CH4" ja 16.04 (self-parametria ei anneta, mutta Python antaa sen __init__-metodille automaattisesti). Käsky palauttaa uuden olion, jonka kentät kaava ja moolimassa on täytetty arvoilla "CH4" ja 16.04.

Kokonainen olioesimerkki

Luokkamäärittelyn pohjalta voi siis luoda mielivaltaisen määrän uusia olioita. Katsotaan kokonaisen esimerkin avulla, miten olioiden kenttiä voi lukea ja miten niiden metodeja käytetään:

class Molekyyli:
    def __init__(self, kaava, moolimassa):
        self.kaava = kaava
        self.moolimassa = moolimassa
       
    def laske_ainemaara(self, massa):
        return massa / self.moolimassa

metaani = Molekyyli("CH4", 16.04) 
etaani = Molekyyli("C2H6", 30.07)

# Käytetään olioiden kenttiä
print("Metaanin molekyylikaava on", metaani.kaava)
print("Etaanin molekyylikaava on", etaani.kaava)
print("Metaanin moolimassa on", metaani.moolimassa, "g/mol")
print("Etaanin moolimassa on", etaani.moolimassa, "g/mol")

# Käytetään laske_ainamaara-metodia. 
# Huomaa, että self-parametria ei anneta
n_metaani = metaani.laske_ainemaara(5.0) # 5 g metaania
n_etaani = etaani.laske_ainemaara(7.0)   # 7 g etaania
print("5 g metaania on", round(n_metaani, 3), "mol")
print("7 g etaania on", round(n_etaani, 3), "mol")

tulostaa

Metaanin molekyylikaava on CH4
Etaanin molekyylikaava on C2H6
Metaanin moolimassa on 16.04 g/mol
Etaanin moolimassa on 30.07 g/mol
5 g metaania on 0.312 mol
7 g metaania on 0.233 mol

Huomaa, miten laske_ainemaara-metodissa self.moolimassa viittaa kunkin olion omaan moolimassa-kenttään. Sillä on siis eri arvo metaanille ja etaanille. Näin ainemäärä lasketaan oikein kullekin oliolle. Parametri massa taas määritetään aina metodia kutsuttaessa.

Kannattaa tutustua esimerkkiin huolella. Esimerkki on yksinkertainen, mutta sen tarkoituksena on havainnollistaa, miten olioiden avulla voidaan yhdistää tietorakenteet ja funktiot samaan pakettiin. self-parametrin käyttö on olio-ohjelmoinnin avainkäsitteitä.

Kenttien luominen käynnistysmetodissa

Luokat voivat sisältää myös kenttiä, joita ei anneta käynnistysmetodin parametrina. On kuitenkin hyvä ohjelmointitapa alustaa myös nämä kentät käynnistysmetodissa. Tarkastellaan luokkaa Laboratorio, jossa kaksi kenttää annetaan käynnistysmetodin parametreina ja yksi kenttä alustetaan käynnistysmetodissa:

class Laboratorio:
    def __init__(self, nimi, kapasiteetti):
        # nimi: laboratorion nimi merkkijonona
        # kapasiteetti: laboratorioon mahtuva opiskelijamäärä (kokonaisluku)
        self.nimi = nimi
        self.kapasiteetti = kapasiteetti
        # Luodaan myös kenttä "opiskelijoita" (kokonaisluku), jonka arvo on alussa 0
        self.opiskelijoita = 0
    
    def lisaa_opiskelija(self):
        # Mahtuuko labraan vielä?
        if self.opiskelijoita < self.kapasiteetti:
            # Kasvatetaan"opiskelijoita"-kenttää yhdellä
            self.opiskelijoita += 1
            print("Opiskelijoita nyt:", self.opiskelijoita)
        else:
            print("Labra on täynnä!")

Kun luokan toteutusta testataan näin:

labra1 = Laboratorio("SEM", 3)
for i in range(4):
    labra1.lisaa_opiskelija()

saadaan tulokseksi

Opiskelijoita nyt: 1
Opiskelijoita nyt: 2
Opiskelijoita nyt: 3
Labra on täynnä!

Jos haluat päästää opiskelijat pois labrasta, voit lisätä luokan toteutukseen metodin vapauta_opiskelija, joka vähentää labrasta yhden opiskelijan.

Yllättävä käänne

Olemme itse asiassa käyttäneet olioita aivan koko kurssin ajan! Pythonissa oikeastaan kaikki asiat ovat olioita. Esimerkiksi int ja float -tyyppiset muuttujat tai list ja dict -tietorakenteet ovat kaikki olioita, jotka sisältävät myös metodeja kyseisten olioiden käsittelemiseksi:

# float-olio sisältää esimerkiksi is_integer()-metodin
liukuluku = 3.14
print(liukuluku.is_integer())
liukuluku_int = 3.0
print(liukuluku_int.is_integer())

tulostaa

False
True

list-tietorakenne sisältävää useita metodeja, joita olemmekin jo käyttäneet

lista = [1, 2, 3]
print(lista.count(1))
lista.append(1)
print(lista.count(1))

tulostaa

1
2