Ohjelmoinnin peruskurssi Y1 CSE-A1111 2.11.2015 CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 1 / 37
Mahdollisuus antaa luentopalautetta Goblinissa vasemmassa reunassa olevassa valikossa on valinta Luentopalaute. Tätä kautta on mahdollisuus antaa nimettömänä palautetta luennosta. Jos jokin asia on jäänyt palautteen perusteella epäselväksi, palataan siihen mahdollisuuksien mukaan seuraavalla luennolla. CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 2 / 37
Oppimistavoitteet: tämän luennon jälkeen Osaat käyttää monipuolisempia olioita kuin mitä viime viikon luentojen perusteella: Osaat kirjoittaa metodin, joka luo ja palauttaa uuden olion (eri asia kuin init -metodi). Tiedät, miksi kurssin esimerkeissä kenttien nimet alkavat kahdella alaviivalla. Osaat esimerkiksi määritellä olioita, joiden kenttinä on listoja. Osaat tehdä listoja, joiden alkiot ovat viitteitä olioihin. CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 3 / 37
Metodi, joka luo ja palauttaa olion Kirjoitetaan viime luennolla esitettyyn luokkaan Tasovektori metodi summavektori, joka laskee kahden vektorin summavektorin. Metodi luo summavektoria varten uuden Tasovektori-olion ja palauttaa sen. Metodi luo uuden Tasovektori-olion samalla tavalla kuin luokan olio luotaisiin luokan ulkopuolella: uusi_vektori = Tasovektori(x_summa, y_summa) Metodi palauttaa return-käskyllä luodun vektorin (tarkasti ottaen viitteen luotuun Tasovektori-olioon. CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 4 / 37
Metodi summavektori (luokan Tasovektori sisällä) def summavektori(self, toinen_vektori): x_summa = self. x_kerroin + \ toinen_vektori. x_kerroin y_summa = self. y_kerroin + \ toinen_vektori. y_kerroin uusi_vektori = Tasovektori(x_summa, y_summa) return uusi_vektori CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 5 / 37
Esimerkki metodia kutsuvasta pääohjelmasta import tasovektori def main(): vektori_a = tasovektori.tasovektori(4.0, 6.5) vektori_b = tasovektori.tasovektori(2.0, -3.5) vektori_c = vektori_b.summavektori(vektori_a) print("{:s} + {:s} = {:s}".format(str(vektori_a), str(vektori_b), str(vektori_c))) main() CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 6 / 37
Välitehtävä 1 Mikä ero olisi, jos metodin summavektori sijaan kirjoitettaisiinkin seuraava metodi (luokan Tasovektori sisälle)? def summaa(self, toinen_vektori): self. x_kerroin = self. x_kerroin + \ toinen_vektori. x_kerroin self. y_kerroin = self. y_kerroin + \ toinen_vektori. y_kerroin CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 7 / 37
Olioiden kenttien näkyvyys Esimerkeissä olioiden kenttien nimet ovat alkaneet kahdella alaviivalla. Tämä saa aikaan sen, että kenttiin ei pääse suoraan käsiksi luokan ulkopuolelta, vaan olion kenttiä on aina käsiteltävä luokan metodien avulla. Seuraavan esimerkin avulla esitetään, mitä hyötyä tästä on. CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 8 / 37
Esimerkki kenttien käsittelystä suoraan Olkoon määritelty luokka henkilötietojen käsittelyyn. class Henkilo1: def init (self, nimi1): self.nimi = nimi1 self.ika = 0 Luokkaa käytetään apuna monessa eri ohjelmassa, esimerkiksi: oppilas = Henkilo1("Matti") oppilas.ika = 15 if oppilas.ika < 18: print("oppilas on alaikainen") else: print("oppilas on taysi-ikainen") oppilas.ika = 16 print("oppilaan ika on", oppilas.ika) CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 9 / 37
Esimerkki kenttien käsittelystä jatkuu Jossain vaiheessa kenttä ika päätetään korvata kentällä syntymavuosi. Luokan määrittely alkaa nyt class Henkilo1: def init (self, nimi1): self.nimi = nimi1 self.syntymavuosi = 0 Millaisia muutostarpeita tämä aiheuttaa niissä ohjelmissa, jotka käyttävät tätä luokkaa? CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 10 / 37
Esimerkki vaihtoehtoisesta tavasta Tarkastellaan vaihtoehtoista tapaa: olioiden kenttiä voi käsitellä vain luokan metodien avulla: class Henkilo2: def init (self, nimi1): self. nimi = nimi1 self. ika = 0 def kerro_ika(self): return self. ika def muuta_ika(self, uusi_ika): if 0 <= uusi_ika <= 150: self. ika = uusi_ika CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 11 / 37
Esimerkki vaihtoehtoisesta tavasta jatkuu Tätäkin luokkaa käytettäisiin apuna monessa ohjelmassa henkilötietojen käsittelyyn: oppilas = Henkilo2("Matti") oppilas.muuta_ika(15) if oppilas.kerro_ika() < 18: print("oppilas on alaikainen") else: print("oppilas on taysi-ikainen") print("oppilaan ika on", oppilas.kerro_ika()) CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 12 / 37
Esimerkki vaihtoehtoisesta tavasta jatkuu Olion kenttä ika korvataan kentällä syntymavuosi: NYKYINEN_VUOSI = 2015 class Henkilo2: def init (self, nimi1): self. nimi = nimi1 self. syntymavuosi = 0 def kerro_ika(self): return NYKYINEN_VUOSI - self. syntymavuosi def muuta_ika(self, uusi): if 0 <= uusi <= 150: self. syntymavuosi = NYKYINEN_VUOSI - uusi Mitä pitää muuttaa tätä luokkaa käyttävissä ohjelmissa? CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 13 / 37
Huomautus Python-kieltä käytettäessä kenttien yksityisyys ei ole aivan niin tärkeää kuin edellä on esitetty, koska luokkaa jälkikäteen muokatessa pystyy Pythonin valmiin property-funktion avulla määräämään sen, että kentän suoran käsittelyn sijasta käytetäänkin määrättyä metodia. Tätä mahdollisuutta ei ole kuitenkaan monessa muussa olio-ohjelmointikielessä (esim. Java). Lisäksi kun olioiden kenttien arvoja muutetaan vain metodien avulla, on metodien yhteyteen helppo lisätä tarkastuksia siitä, että uusi arvo on järkevä. CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 14 / 37
Lista olion kenttänä Olion kenttä voi olla myös esimerkiksi lista, sanakirja tai oliomuuttuja. Seuraavassa esimerkissä on määritelty luokka Bonusasiakas jonkin kaupan kanta-asiakkaan kuvaamiseen. Luokan olioilla on kentät nimi ja ostokset. Jälkimmäinen on lista, joka sisältää kanta-asiakkaan eri kertaostosten arvot. Uutta asiakasta luodessa lista ostokset alustetaan tyhjäksi listaksi. Listaan voi lisätä ostoksia luokan metodin avulla. CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 15 / 37
Luokka Bonusasiakas, koodi class Bonusasiakas: def init (self, asiakkaan_nimi): self. nimi = asiakkaan_nimi self. ostokset = [] def kerro_nimi(self): return self. nimi def lisaa_ostos(self, arvo): if arvo > 0.0: self. ostokset.append(arvo) return True else: return False CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 16 / 37
Luokka Bonusasiakas, koodi jatkuu 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 def laske_rajan_ylittaneet(self, alaraja): ylittaneiden_lkm = 0 for arvo in self. ostokset: if arvo > alaraja: ylittaneiden_lkm += 1 return ylittaneiden_lkm CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 17 / 37
Luokka Bonusasiakas, koodi jatkuu 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 def nollaa_ostokset(self): self. ostokset = [] CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 18 / 37
Luokka Bonusasiakas, koodi jatkuu def str (self): mjono = "Asiakas: " + self. nimi + "\nostot:\n" for arvo in self. ostokset: mjono += "{:.2f} eur\n".format(arvo) return mjono CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 19 / 37
Bonusasiakas-olioita käsittelevä pääohjelma 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 CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 20 / 37
Bonusasiakas-olioita käsittelevä pääohjelma jatkuu 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("anna asiakkaan {:s} yhden kertaoston arvo.".\ format(asiakas1.kerro_nimi())) luettu_arvo = lue_desimaaliluku() if asiakas1.lisaa_ostos(luettu_arvo): print("ostoksen lisays onnistui.") else: print("ostoksen lisays ei onnistunut.") CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 21 / 37
Bonusasiakas-olioita käsittelevä pääohjelma for i in range(ostoskerrat): print("anna asiakkaan {:s} yhden kertaoston arvo.".\ format(asiakas2.kerro_nimi())) luettu_arvo = lue_desimaaliluku() if asiakas2.lisaa_ostos(luettu_arvo): print("ostoksen lisays onnistui.") else: print("ostoksen lisays ei onnistunut.") print("1. asiakkaan ostosten keskiarvo: {:.2f} eur".\ format(asiakas1.laske_keskiarvo())) print("2. asiakkaan ostosten keskiarvo: {:.2f} eur".\ format(asiakas2.laske_keskiarvo())) CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 22 / 37
Bonusasiakas-olioita käsittelevä pääohjelma jatkuu print("1. asiakkaan bonus: {:.2f} eur".format(\ asiakas1.laske_bonus())) print("2. asiakkaan bonus: {:.2f} eur".format(\ asiakas2.laske_bonus())) print("anna raja, jonka ylittavat ostokset haetaan.") raja = lue_desimaaliluku() ylitykset1 = asiakas1.laske_rajan_ylittaneet(raja) ylitykset2 = asiakas2.laske_rajan_ylittaneet(raja) print("1. asiakkaalla oli {:d} suurempaa ostosta".\ format(ylitykset1)) print("2. asiakkaalla oli {:d} suurempaa ostosta".\ format(ylitykset2)) CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 23 / 37
Bonusasiakas-olioita käsittelevä pääohjelma jatkuu print("asiakkaiden tiedot:") print(asiakas1) print(asiakas2) asiakas1.nollaa_ostokset() print("1. asiakas nollauksen jalkeen:") print(asiakas1) main() CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 24 / 37
Välitehtävä 2 Kirjoita luokkaan Bonusasiakas metodi hae_suurin_ostos, joka hakee asiakkaan suurimman ostoksen arvon ja palauttaa sen. Jos asiakkaalla ei ole lainkaan ostoksia, metodin pitää palauttaa 0.0. CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 25 / 37
Listan alkiona viiteitä olioihin Halutaan tehdä ohjelma ohjelmointikurssien opiskelijoiden käsittelyyn. Yhden opiskelijan tietoja kuvataan yhdellä Opiskelija-oliolla. Koska opiskelijoita voi olla kymmeniä tai satoja, ei ole järkevää viitata jokaiseen Opiskelija-olioon omalla muuttujalla. Tehdään sen sijaan lista, joka sisältää viitteet kurssin kaikkia opiskelijoita kuvaaviin Opiskelija-olioihin. CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 26 / 37
Olioviitteiden lisääminen listaan Listaan voi lisätä uuden Opiskelija-olion esimerkiksi seuraavasti: opiskelijalista.append(opiskelija("matti Virtanen", \ "511114")) Toinen vaihtoehto on tehdä olion luonti ja sen lisääminen listaan eri käskyissä: uusi_opiskelija = Opiskelija("Matti Virtanen", "511114") opiskelijalista.append(uusi_opiskelija) Jos listassa on jo vähintään i+1 alkiota, voi olioviitteen sijoittaa myös suoraan paikalle i: uusi_opiskelija = Opiskelija("Matti Virtanen", "511114") opiskelijalista[i] = uusi_opiskelija CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 27 / 37
Metodien kutsuminen listan alkioille Indeksillä i olevalle Opiskelija-oliolle voidaan kutsua Opiskelija-luokan metodeita seuraavasti: print("opiskelijan", opiskelijalista[i].kerro_nimi()) print("kurssiarvosana on", opiskelijalista[i].laske_kokonaisarvosana()) On myös mahdollista käydä koko lista läpi for-käskyllä ja kutsua metodeita vuorotellen jokaiselle listan alkiolle: for kurssilainen in opiskelijalista: print("opiskelijan", kurssilainen.kerro_nimi()) print("kurssiarvosana on", kurssilainen.laske_kokonaisarvosana()) CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 28 / 37
Opiskelija-olioita listassa Seuraavilla kalvoilla on esimerkkiohjelma, joka lukee käyttäjältä opiskelijoiden tiedot, luo vastaavat Opiskelija-oliot ja sijoittaa ne listaan, pyytää listan opiskelijoille arvosanat ja tulostaa kurssin tulokset. Eri vaiheita varten on kirjoitettu omat funktionsa. Aluksi on jo aikaisemmin esitetty Opiskelija-luokan koodi. CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 29 / 37
Opiskelija-luokka class Opiskelija: def init (self, annettu_nimi, numero): self. nimi = annettu_nimi self. opiskelijanumero = numero self. tenttiarvosana = 0 self. harjoitusarvosana = 0 def kerro_nimi(self): return self. nimi def kerro_opiskelijanumero(self): return self. opiskelijanumero CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 30 / 37
Opiskelija-luokka jatkuu def kerro_tenttiarvosana(self): return self. tenttiarvosana def kerro_harjoitusarvosana(self): return self. harjoitusarvosana def muuta_tenttiarvosana(self, arvosana): if 0 <= arvosana <= 5: self. tenttiarvosana = arvosana def muuta_harjoitusarvosana(self, arvosana): if 0 <= arvosana <= 5: self. harjoitusarvosana = arvosana CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 31 / 37
Opiskelija-luokka jatkuu def laske_kokonaisarvosana(self): if self. tenttiarvosana == 0 or \ self. harjoitusarvosana == 0: arvosana = 0 else: arvosana = (self. tenttiarvosana + self. harjoitusarvosana + 1) // 2 return arvosana def str (self): mjono = self. nimi + ", " + \ self. opiskelijanumero + \ ", tenttiarvosana: " + \ str(self. tenttiarvosana) + \ ", harjoitusarvosana: " + \ str(self. harjoitusarvosana) return mjono CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 32 / 37
Opiskelija-olioita listassa, koodi import opiskelija def lue_kokonaisluku(): luku_onnistui = False while not luku_onnistui: try: luku = int(input()) luku_onnistui = True except ValueError: print("virheellinen kokonaisluku!") print("anna uusi!") return luku CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 33 / 37
Opiskelija-olioita listassa, koodi jatkuu def lue_opiskelijatiedot(): opiskelijat = [] print("anna opiskelijoiden nimet ja opiskelijanumerot") print("samalla rivilla kauttaviivalla erotettuna.") print("lopeta tyhjalla rivilla.") rivi = input() while rivi!= "": tiedot = rivi.split("/") if len(tiedot)!= 2: print("virheellinen rivi!") else: uusi = opiskelija.opiskelija(tiedot[0], tiedot[1]) opiskelijat.append(uusi) rivi = input() print("opiskelijoiden tiedot luettu!") return opiskelijat CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 34 / 37
Opiskelija-olioita listassa, koodi jatkuu def lisaa_arvosanatiedot(opiskelijalista): for kurssilainen in opiskelijalista: print("anna opiskelijan {:s} tenttiarvosana:".\ format(kurssilainen.kerro_nimi())) tentti_as = lue_kokonaisluku() print("harjoitusarvosana:") harjoitus_as = lue_kokonaisluku() kurssilainen.muuta_tenttiarvosana(tentti_as) kurssilainen.muuta_harjoitusarvosana(harjoitus_as) print("arvosanat lisatty!") CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 35 / 37
Opiskelija-olioita listassa, koodi jatkuu def tulosta_tulokset(opiskelijat): print("numero nimi tentti harj kurssi") for i in range(len(opiskelijat)): print("{:6s} {:15s} {:<6d} {:<6d} {:<6d}".format(\ opiskelijat[i].kerro_opiskelijanumero(), opiskelijat[i].kerro_nimi(), opiskelijat[i].kerro_tenttiarvosana(), opiskelijat[i].kerro_harjoitusarvosana(), opiskelijat[i].laske_kokonaisarvosana())) CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 36 / 37
Opiskelija-olioita listassa, koodi jatkuu def main(): opiskelijatiedot = lue_opiskelijatiedot() lisaa_arvosanatiedot(opiskelijatiedot) tulosta_tulokset(opiskelijatiedot) print("ohjelma paattyy.") main() CSE-A1111 Ohjelmoinnin peruskurssi Y1 2.11.2015 37 / 37