Lista olion kenttänä

Olion kenttien ei tarvitse olla pelkästään lukuja ja merkkijonoja, vaan kenttänä voi olla esimerkiksi lista tai jokin muu oliomuuttuja. Tarkastellaan seuraavaksi esimerkkiä, jossa halutaan tehdä ohjelma kaupan kanta-asiakasrekisterin hallintaan. Kirjoitetaan luokka Bonusasiakas yhden kanta-asiakkaan kuvaamiseen. (Varsinaista rekisteriohjelmaa ei ole esitetty tässä materiaalissa.)

Luokan oliolla on kenttä asiakkaan nimeä varten sekä toinen kenttä __ostokset. Jälkimmäinen kenttä on lista, joka sisältää asiakkaan tarkasteltavana ajanjaksona tekemien kertaostosten arvot.

Metodissa __init__ annetaan parametrin mukainen alkuarvo asiakkaan nimelle ja luodaan tyhjä lista, joka asetetaan __ostokset-kentän arvoksi.

Metodi lisaa_ostos lisää __ostokset-listaan yhden uuden ostoksen. Käytännössä se lisää listan loppuun uuden alkion, jonka arvo annetaan metodin parametrina. Parametrin pitää kuitenkin olla positiivinen. Jos parametrina annettu arvo on negatiivinen tai nolla, ostosta ei lisätä listaan lainkaan. Metodi palauttaa arvon True tai False sen mukaan, onnistuiko ostoksen lisääminen (parametri sallitulla välillä) vai ei. Tätä tapaa voi yleisemminkin käyttää, kun pitää välittää tieto metodin suorituksen onnistumisesta ohjelman siihen kohtaan, jossa metodia kutsutaan.

Metodi laske_keskiarvo laskee asiakkaan tekemien ostosten keskiarvon ja palauttaa sen. Jos asiakkaalla ei ole lainkaan ostoksia (lista __ostokset on tyhjä), metodi palauttaa arvon 0.0.

Metodi laske_rajan_ylittaneet käy läpi listan __ostokset ja laskee, kuinka moni siinä olevista ostoksista ylittää parametrina annetun rajan. Kauppa voi käyttää tätä metodia silloin, kun se haluaa etsiä asiakkaita, jotka tekevät paljon suuria kertaostoksia.

Metodi laske_bonus laskee ja palauttaa asiakkaalle tarkasteltavan ajanjakson aikana kertyneen bonuksen.

Metodi nollaa_ostokset tyhjentää asiakkaan __ostokset-listan. Tarkasti ottaen metodi luo uuden, tyhjän listan, ja panee __ostokset-kentän viittaamaan siihen. Tätä metodia on tarkoitus käyttää tarkasteltavan aikajakson lopussa sen jälkeen, kun asiakkaan bonukset on laskettu ja ostotiedot halutaan nollata seuraavaa aikajaksoa varten.

Metodi __str__ on kirjoitettu niin, että se lisää palauttamaansa merkkijonoon asiakkaan nimen ja ostokset, kunkin ostoksen omalle rivilleen. Rivinvaihdot saadaan aikaan lisäämällä merkkijonoon "\n"-merkkejä. Ostosten arvot saadaan merkkijonoon kahden desimaalin tarkkuudella käyttämällä f-stringejä.

Seuraavaksi koko luokan koodi:

# Luokka eraan kaupan yhden kanta-asiakkaan kuvaamiseen.

class Bonusasiakas:

    # Metodi luo uuden bonusasiakkaan. Luotavan asiakkaan nimi
    # annetaan metodin parametrina.

    def __init__(self, asiakkaan_nimi):
        self.__nimi = asiakkaan_nimi
        self.__ostokset = []


    # Metodi palauttaa bonusasiakkaan nimen.

    def kerro_nimi(self):
        return self.__nimi


    # Metodi lisaa bonusasiakkalle yhden kertaostoksen. Ostoksen
    # arvo annetaan metodin parametrina. Metodi palauttaa arvon
    # True, jos ostos voidaan lisata, ja muuten arvon False.

    def lisaa_ostos(self, arvo):
        if arvo > 0.0:
            self.__ostokset.append(arvo)
            return True
        else:
            return False


    # Metodi laskee ja palauttaa bonusasiakkaan kertaostosten
    # keskiarvon.

    def laske_keskiarvo(self):
        lkm = 0
        summa = 0.0
        for ostoksen_arvo in self.__ostokset:
            summa += ostoksen_arvo
            lkm += 1
        if lkm == 0:
            return 0.0
        else:
            return summa / lkm


    # Metodi laskee, kuinka moni bonusasiakkaan kertaostoksista
    # ylittaa arvoltaan parametrina annetun rajan. Metodi palauttaa
    # naiden kertaostosten lukumaaran.

    def laske_rajan_ylittaneet(self, alaraja):
        ylittaneiden_lkm = 0
        for arvo in self.__ostokset:
            if arvo > alaraja:
                ylittaneiden_lkm += 1
        return ylittaneiden_lkm


    # Metodi laskee ja palauttaa asiakkaalle tulevan
    # bonuksen, joka maaraytyy ostosten kokonaisarvon perusteella.

    def laske_bonus(self):
        PIENIBONUS = 1.0
        SUURIBONUS = 2.5
        BONUSRAJA = 400.0
        summa = 0.0
        for ostos in self.__ostokset:
            summa += ostos
        if summa >= BONUSRAJA:
            bonus = SUURIBONUS * summa / 100.0
        else:
            bonus = PIENIBONUS * summa / 100.0
        return bonus


    # Metodi nollaa bonusasiakkaan ostostiedot.

    def nollaa_ostokset(self):
        self.__ostokset = []


    # Metodi palauttaa merkkijonon, joka sisaltaa asiakkaan
    # nimen ja ostokset, kunkin kertaostoksen arvon omalla rivillaan.

    def __str__(self):
        mjono = "Asiakas: " + self.__nimi + "\nOstot:\n"
        for arvo in self.__ostokset:
            mjono += f"{arvo:.2f} eur\n"
        return mjono

Seuraavaksi esitetään esimerkki pääohjelmasta, joka luo kaksi Bonusasiakas-oliota ja suorittaa näille erilaisia toimenpiteitä. Ohjelmassa on jälleen laadittu apufunktio desimaaliluvun lukemiseen käyttäjältä. Ohjelma kannattaisi jakaa useampaan funktioon, mutta se on tässä jätetty tekemättä, jotta olioiden luonti ja niiden metodien kutsuminen olisi mahdollisimman selkeätä myös aloittelijalle.

Esimerkistä nähdään myös se, miten metodin lisaa_ostos paluuarvoa voidaan käyttää hyväksi. Koska metodi palauttaa arvon True tai False, voidaan sen kutsu kirjoittaa suoraan if-käskyn ehdoksi. Metodin kutsu if-käskyn ehtona saa aikaan metodin suorittamisen. Lisäksi metodin paluuarvoa käytetään saman tien ratkaisemaan, suoritetaanko if-käskyn sisällä oleva käsky.

import bonusasiakas

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():
    OSTOSKERRAT = 4
    nimi1 = input("Anna 1. asiakkaan nimi: ")
    asiakas1 = bonusasiakas.Bonusasiakas(nimi1)
    nimi2 = input("Anna 2. asiakkaan nimi: ")
    asiakas2 = bonusasiakas.Bonusasiakas(nimi2)
    for i in range(OSTOSKERRAT):
        print(f"Anna asiakkaan {asiakas1.kerro_nimi():s} yhden kertaoston arvo.")
        luettu_arvo = lue_desimaaliluku()
        if asiakas1.lisaa_ostos(luettu_arvo):
            print("Ostoksen lisays onnistui.")
        else:
            print("Ostoksen lisays ei onnistunut.")
    for i in range(OSTOSKERRAT):
        print(f"Anna asiakkaan {asiakas2.kerro_nimi():s} yhden kertaoston arvo.")
        luettu_arvo = lue_desimaaliluku()
        if asiakas2.lisaa_ostos(luettu_arvo):
            print("Ostoksen lisays onnistui.")
        else:
            print("Ostoksen lisays ei onnistunut.")

    print(f"1. asiakkaan ostosten keskiarvo: {asiakas1.laske_keskiarvo():.2f} eur")
    print(f"2. asiakkaan ostosten keskiarvo: {asiakas2.laske_keskiarvo():.2f} eur")

    print(f"1. asiakkaan bonus: {asiakas1.laske_bonus():.2f} eur")
    print(f"2. asiakkaan bonus: {asiakas2.laske_bonus():.2f} eur")

    print("Anna raja, jonka ylittavat ostokset haetaan.")
    raja = lue_desimaaliluku()
    ylitykset1 = asiakas1.laske_rajan_ylittaneet(raja)
    ylitykset2 = asiakas2.laske_rajan_ylittaneet(raja)
    print(f"1. asiakkaalla oli {ylitykset1:d} suurempaa ostosta")
    print(f"2. asiakkaalla oli {ylitykset2:d} suurempaa ostosta")

    print("Asiakkaiden tiedot:")
    print(asiakas1)
    print(asiakas2)

    asiakas1.nollaa_ostokset()
    print("1. asiakas nollauksen jalkeen:")
    print(asiakas1)


main()

Alla on esimerkki ohjelman suorituksesta:

Anna 1. asiakkaan nimi: Anna Lahti
Anna 2. asiakkaan nimi: Roope Rikas
Anna asiakkaan Anna Lahti yhden kertaoston arvo.
45.0
Ostoksen lisays onnistui.
Anna asiakkaan Anna Lahti yhden kertaoston arvo.
-12.8
Ostoksen lisays ei onnistunut.
Anna asiakkaan Anna Lahti yhden kertaoston arvo.
0.0
Ostoksen lisays ei onnistunut.
Anna asiakkaan Anna Lahti yhden kertaoston arvo.
15.0
Ostoksen lisays onnistui.
Anna asiakkaan Roope Rikas yhden kertaoston arvo.
120.0
Ostoksen lisays onnistui.
Anna asiakkaan Roope Rikas yhden kertaoston arvo.
10.30
Ostoksen lisays onnistui.
Anna asiakkaan Roope Rikas yhden kertaoston arvo.
120.4
Ostoksen lisays onnistui.
Anna asiakkaan Roope Rikas yhden kertaoston arvo.
180.0
Ostoksen lisays onnistui.
1. asiakkaan ostosten keskiarvo: 30.00 eur
2. asiakkaan ostosten keskiarvo: 107.68 eur
1. asiakkaan bonus: 0.60 eur
2. asiakkaan bonus: 10.77 eur
Anna raja, jonka ylittavat ostokset haetaan.
44.0
1. asiakkaalla oli 1 suurempaa ostosta
2. asiakkaalla oli 3 suurempaa ostosta
Asiakkaiden tiedot:
Asiakas: Anna Lahti
Ostot:
45.00 eur
15.00 eur

Asiakas: Roope Rikas
Ostot:
120.00 eur
10.30 eur
120.40 eur
180.00 eur

1. asiakas nollauksen jalkeen:
Asiakas: Anna Lahti
Ostot: