Olio metodin parametrina: luokka Tasovektori

Halutaan kirjoittaa ohjelma, jonka avulla voi käsitellä kaksiulotteisia vektoreita. Vektorit ovat siis muotoa \(a\bar{i} + b\bar{j}\), missä \(a\) kertoo vektorin x-akselin suuntaisen komponentin kertoimen ja \(b\) vektorin y-akselin suuntaisen komponentin kertoimen. Kirjoitetaan luokka Tasovektori kuvaamaan tällaisia vektoreita. Luokan koodissa edellä mainittuja kertoimia on kuvattu kentillä __x_kerroin ja __y_kerroin.

Yksi vektoreille usein tarvittava toimenpide on pistetulon laskeminen. Pistetulo lasketaan kahden vektorin välillä. Kun siis määritellään pistetuloa laskevaa metodia pistetulo, pitää sille välittää tieto kahdesta eri vektorista, joiden välillä pistetulo lasketaan. Toinen vektoreista on luonnollisesti metodin ensimmäinen parametri self, joka annetaan metodia kutsuttaessa ennen pistettä ja metodin nimeä. Toinen vektori voidaan välittää metodille parametrina ihan samalla tavalla kuin metodille annetaan parametrina esimerkiksi kokonaisluku tai desimaaliluku.

Pistetulon laskevassa metodissa pitää selvittää molemmista vektoreissa niiden __x_kerroin ja __y_kerroin-kenttien arvot. Koska myös parametrina annettu toinen_vektori viittaa saman Tasovektori-luokan olioon, voidaan tämä tehdä suoraan käyttämällä pistenotaatiota seuraavasti:

def pistetulo(self, toinen_vektori):
    tulo = self.__x_kerroin * toinen_vektori.__x_kerroin + \
           self.__y_kerroin * toinen_vektori.__y_kerroin
    return tulo

Tässä on siis selvitetty jonkin olion kentän arvo siten, että on annettu ensin sen oliomuuttujan nimi, joka viittaa käsiteltävään olioon, sitten piste ja sen jälkeen halutun kentän nimi. Tässä nimi self tarkoittaa sitä oliota, jolle metodia kutsutaan (johon viittaavan muuttujan nimi on metodin kutsussa ennen pistettä) ja nimi toinen_vektori metodille parametrina annettua oliota (sitä, jonka nimi on metodin kutsussa sulkujen sisällä).

Toinen tapa on käyttää kentän arvon selvittämiseen olion luokan omaa metodia. Alla olevassa metodissa parametrina annetun olion kenttien arvot on selvitetty käyttämällä saman luokan metodeita kerro_x_kerroin ja kerro_y_kerroin. Tätä tapaa voidaan käyttää myös silloin, kun parametri viittaa jonkin toisen luokan olioon.

def pistetulo2(self, toinen_vektori):
    tulo = self.__x_kerroin * toinen_vektori.kerro_x_kerroin() + \
           self.__y_kerroin * toinen_vektori.kerro_y_kerroin()
    return tulo

Jos siis metodia kutsutaan esimerkiksi

a_vektori.pistetulo(b_vektori)

tai

a_vektori.pistetulo2(b_vektori)

niin molemmissa tapauksissa metodia lähdetään suorittamaan siten, että metodissa nimi self tarkoittaa sitä oliota, johon muuttuja a_vektori viittaa ja nimi toinen_vektori sitä oliota, johon muuttuja b_vektori viittaa.

Alla olevassa animaatiossa on vielä kuvattu metodin pistetulo toimintaa. Animaatiossa on vain osa luokan Tasovektori koodista.

Seuraavaksi luokan Tasovektori koodi kokonaan. Luokkaan on jätetty vain yksi versio pistetulon laskevasta metodista. Sen lisäksi luokkaan on määritelty metodi laske_pituus, joka laskee vektorin pituuden Pythagoraan lauseen avulla. Laskemisessa tarvitaan neliöjuuren laskevaa funktiota, minkä takia luokassa on otetty käyttöön Pythonin valmis moduuli math ja siihen kuuluva neliöjuuren laskeva funktio sqrt. Luokassa on myös metodi kerro_luvulla, joka kertoo vektorin molemmat komponentit parametrina annetulla luvulla. Lisäksi luokkaan on määritelty metodi __str__ tekemään oliosta merkkijonoesitys. Vektoria kuvaava merkkijono on koottu niin, että jos y-akselin suuntaisen komponentin kerroin on negatiivinen, vektoria kuvaavassa merkkijonossa on komponenttien välillä vain yksi miinusmerkki eikä plus- ja miinusmerkkiä peräkkäin. Metodissa on käytetty myös f-stringejä muotoilemaan merkkijonoesitykseen sijoitettavia kertoimien arvoja niin, että ne esitetään kolmen desimaalin tarkkuudella.

import math

# Luokka kuvaa yhta 2-ulotteista vektoria.

class Tasovektori:

    # Metodi __init__ alustaa vektorin kertoimet. Halutut
    # kertoimet annetaan metodin parametrina.

    def __init__(self, eka_kerroin, toka_kerroin):
        self.__x_kerroin = eka_kerroin
        self.__y_kerroin = toka_kerroin


    # Metodi palauttaa vektorin x-akselin suuntaisen komponentin
    # kertoimen.

    def kerro_x_kerroin(self):
        return self.__x_kerroin


    # Metodi palauttaa vektorin y-akselin suuntaisen komponentin
    # kertoimen.

    def kerro_y_kerroin(self):
        return self.__y_kerroin


    # Metodi laskee ja palauttaa vektorin pituuden. Pituus
    # lasketaan Pythagoraan lauseen avulla.

    def laske_pituus(self):
        return math.sqrt(self.__x_kerroin ** 2 + self.__y_kerroin ** 2)


    # Metodi kertoo vektorin molemmat komponentit parametrina annetulla
    # luvulla.

    def kerro_luvulla(self, kertoja):
        self.__x_kerroin *= kertoja
        self.__y_kerroin *= kertoja


    # Metodi laskee vektorin ja parametrina annetun vektori pistetulon.
    # Metodi palauttaa lasketun pistetulon.

    def pistetulo(self, toinen_vektori):
        tulo = self.__x_kerroin * toinen_vektori.kerro_x_kerroin() + \
               self.__y_kerroin * toinen_vektori.kerro_y_kerroin()
        return tulo


    # Metodi palauttaa merkkijonon, joka kuvaa vektoria.

    def __str__(self):
        if self.__y_kerroin >= 0:
            mjono = f"{self.__x_kerroin:.3f}i + {self.__y_kerroin:.3f}j"
        else:
            mjono = f"{self.__x_kerroin:.3f}i - {-self.__y_kerroin:.3f}j"
        return mjono

Alla on esimerkki pääohjelmasta, joka pyytää käyttäjältä kahden vektorin tiedot ja suorittaa näille vektoreille erilaisia toimenpiteitä. Ohjelma lukee käyttäjältä desimaalilukuja. Sitä varten on jälleen määritelty apufunktio, joka sisältää myös mahdollisten virheellisten syötteiden käsittelyn. Pääohjelmassa on oletettu, että luokka Tasovektori on tallennettu omaan moduuliinsa, jonka nimi on tasovektori.

import tasovektori

def lue_desimaaliluku():
    luku_onnistui = False
    while not luku_onnistui:
        try:
            luku = float(input())
            luku_onnistui = True
        except ValueError:
            print("Virheellinen desimaaliluku!")
            print("Anna uusi!")
    return luku

def main():
    print("Anna ensimmaisen vektorin i-kerroin.")
    i_kerroin = lue_desimaaliluku()
    print("Anna ensimmaisen vektorin j-kerroin.")
    j_kerroin = lue_desimaaliluku()
    a_vektori = tasovektori.Tasovektori(i_kerroin, j_kerroin)
    print("Antamasi vektori on", a_vektori)

    print("Anna toisen vektorin i-kerroin.")
    i_kerroin = lue_desimaaliluku()
    print("Anna toisen vektorin j-kerroin.")
    j_kerroin = lue_desimaaliluku()
    b_vektori = tasovektori.Tasovektori(i_kerroin, j_kerroin)
    print("Antamasi vektori on", b_vektori)

    pituus1 = a_vektori.laske_pituus()
    pituus2 = b_vektori.laske_pituus()
    print(f"Vektorin {a_vektori} pituus on {pituus1:.3f}")
    print(f"Vektorin {b_vektori} pituus on {pituus2:.3f}")

    laskettu_tulo = a_vektori.pistetulo(b_vektori)
    print(f"Vektoreiden pistetulo on {laskettu_tulo:.3f}.")

    print("Milla luvulla ensimmainen vektori kerrotaan?")
    kerroin = lue_desimaaliluku()
    a_vektori.kerro_luvulla(kerroin)
    print("Ensimmainen vektori kertomisen jalkeen", a_vektori)


main()

Esimerkki ohjelman suorituksesta:

Anna ensimmaisen vektorin i-kerroin.
4.5
Anna ensimmaisen vektorin j-kerroin.
-2.3
Antamasi vektori on 4.500i - 2.300j
Anna toisen vektorin i-kerroin.
3.3
Anna toisen vektorin j-kerroin.
5.0
Antamasi vektori on 3.300i + 5.000j
Vektorin 4.500i - 2.300j pituus on 5.054
Vektorin 3.300i + 5.000j pituus on 5.991
Vektoreiden pistetulo on 3.350.
Milla luvulla ensimmainen vektori kerrotaan?
-5.0
Ensimmainen vektori kertomisen jalkeen -22.500i + 11.500j

Luokan metodien sisällä voidaan kutsua saman luokan metodeita myös käsiteltävälle oliolle self. Esimerkiksi luokkaan Tasovektori voitaisiin kirjoittaa metodi onko_pitempi, joka tutkii, onko käsiteltävä vektori pitempi kuin parametrina saatu vektori. Vektoreiden pituuksien laskemisessa voidaan käyttää hyväksi luokassa jo määriteltyä metodia laske_pituus:

def onko_pitempi(self, toinen_vektori):
    if self.laske_pituus() > toinen_vektori.laske_pituus():
        return True
    else:
        return False
Tämä laske_pituus-metodin kutsu laskee käsiteltävän Tasovektori-olion pituuden (sen, joka on annettu metodia onko_pitempi kutsuttaessa ennen pistettä).
Tämä laske_pituus-metodin kutsu laskee parametrina annetun Tasovektori-olion pituuden.

Toki pituudet voitaisiin laskea suoraankin vektoreiden kenttien arvoista käyttämättä lainkaan laske_pituus-metodia.

Metodia onko_pitempi voitaisiin käyttää lisämäällä aikaisemmin esitettyyn pääohjelmaan esimerkiksi seuraavat rivit:

if a_vektori.onko_pitempi(b_vektori):
    print("Ensimmainen vektori on pitempi.")
else:
    print("Ensimmainen vektori ei ole pitempi kuin toinen.")