15. Objektorienteeritud programmeerimine 2
Eelmises peatükis uurisime lähemalt, kuidas luua klasse ja defineerida klassi konstruktorit, meetodeid ja muutujaid. Seni aga puuduvad meil teadmised, kuidas efektiivselt luua klasse, mis on sisu poolest väga sarnased, ent sisaldavad väikseid eripärasid. Selles peatükis uurime, kuidas luua alam- ja ülemklasse, mis on pärilus ja polümorfism ning kuidas mugavamalt objekti infot väljastada.
Alam- ja ülemklass
Erinevaid klasse luues võib meil tekkida olukord, kus tahame luua klasse, mis on sarnased, ent nendes esineb eripärasid. Hetkel peaksime kõik klassid looma eraldi ja defineerima kõikides klassides eraldi meetodid. Selleks, et seda tööd lihtsustada, saame kasutada alam- ja ülemklasse. Alamklassid on klassid, mis pärivad kõik muutujad ja meetodid ülemklassidest.
Selleks, et luua alamklasse, on meil kõigepealt vaja luua üks ülemklass. Loome allpool klassi Koer
, mille isenditel on järgmised omadused: nimi, vanus ja lemmiksöök. Lisaks defineerime meetodi hääl
, mis tagastab koera haukumise hääle.
class Koer: def __init__(self, nimi, vanus, lemmiksöök): self.nimi = nimi self.vanus = vanus self.lemmiksöök = lemmiksöök def hääl(self): return "Auh Auh"
Ülemklassi loomise järel saame nüüd ka luua alamklasse. Alamklassi loomiseks on vaja defineerida uus klass unikaalse nimega ja panna klassi nime järele sulud, mille sees on ülemklassi nimi. Meie näites on ülemklassiks Koer
, seega sulgudesse peab lisama selle. Kui alamklassi midagi ei lisata, siis peab sisuks kirjutama võtmesõna pass
.
Loome kaks ülemklassi Koer
alamklassi, mille nimedeks on kaks erinevat koeratõugu, näiteks KingCharlesCavalierSpaniel
ja SaksaLambakoer
.
class KingCharlesCavalierSpaniel(Koer): pass class SaksaLambakoer(Koer): pass
Loome nüüd mõned alamklasside isendid ja vaatame, mis funktsionaalsus neile ülemklassist pärineb.
carlos = KingCharlesCavalierSpaniel("Carlos", 4, "vorst") ralf = SaksaLambakoer("Ralf", 2, "kohupiim") print(carlos.hääl()) print(carlos.nimi) print() print(ralf.hääl()) print(ralf.lemmiksöök)
Auh Auh Carlos Auh Auh kohupiim
Pärilus ja polümorfism
Eelmise näite lõpus puutusime esmakordselt kokku pärilusega (ingl inheritance). Päriluse abi saab alamklass kasutada ülemklassi muutujaid ja meetodeid, st need pärandatakse ülemklassilt alamklassile. Selleks, et klass saaks teise klassi meetodeid ja muutujaid pärida, on vaja alamklassi defineerides lisada pärast klassi nime sulud, kuhu lisatakse ülemklassi nimi.
Alamklassid saavad kasutada kõiki meetodeid, mis on defineeritud ülemklassis, aga vajadusel ka lisada uusi meetodeid, mis on unikaalsed selle alamklassi jaoks ja üle katta (st üle kirjutada, ingl. override) ülemklassi meetodeid.
Koerte näites nägime, et alamklassid pärisid ülemklassist meetodi hääl
ja samuti kõik ülemklassi muutujad. Proovime nüüd samades alamklassides luua uusi meetodeid ja üle katta ülemklassi meetodi.
class KingCharlesCavalierSpaniel(Koer): def jalutama(self, pikkus_km): aeg = pikkus_km / 10 * 60 return round(aeg) class SaksaLambakoer(Koer): def hääl(self): return "Vuff Vuff" carlos = KingCharlesCavalierSpaniel("Carlos", 4, "vorst") ralf = SaksaLambakoer("Ralf", 2, "kohupiim") print("Carlose jalutuskäik kestis", carlos.jalutama(10), "minutit.") print("Carlose hääl: ", carlos.hääl()) print() print("Ralfi hääl: ", ralf.hääl())
Carlose jalutuskäik kestis 60 minutit. Carlose hääl: Auh Auh Ralfi hääl: Vuff Vuff
Näeme, et alamklassi SaksaLambakoer
isendite puhul erineb meetod hääl
ülemklassi omast, sest katsime selle üle. Lisasime ka alamklassile KingCharlesCavalierSpaniel
meetodi jalutama
, mis on täiesti unikaalne selle alamklassi jaoks: seda ei leidu ei ülemklassis Koer
ega ka teises alamklassis SaksaLambaKoer
.
Ülemklassi meetodite ülekatmise nimetatakse polümorfismiks. Polümorfism tähendab, et ülemklassis defineeritud meetod on alamklassis üle kirjutatud ja seetõttu käitub alamklassi isend erinevalt. Polümorfism on kasulik, kui meil on vaja, et ülemklassis olevad meetod(id) käituksid erinevalt alamklasside meetodist. Alamklassis SaksaLambakoer
ülekaetud meetod hääl
on näide polümorfismist.
Enesekontroll
Meetod __str__
Pärast klassi isendi loomist võib tekkida vajadus väljastada ekraanile info loodud klassi isendi kohta. Proovime väljastada ekraanile klasside KingCharlesCavalierSpaniel
ja SaksaLambakoer
isendid.
carlos = KingCharlesCavalierSpaniel("Carlos", 4, "vorst") ralf = SaksaLambakoer("Ralf", 2, "kohupiim") print(carlos) print(ralf)
<__main__.KingCharlesCavalierSpaniel object at 0x04090DD0> <__main__.SaksaLambakoer object at 0x03640DF0>
Näeme, et klassi isendi printimisel tekib ekraanile segane tekst, justkui krüpteeritud kujul. Tegelikkuses väljastatakse ekraanile klassi isendi tüüp ning tema asukoht arvuti mälus. Kuid sellisest väljundist pole meie jaoks mingit abi. Selleks, et klassi isendite kohta väljastada vajalikku infot, võime luua meetodi nimega __str__
.
Meetod __str__
hoolitseb selle eest, mis väljastatakse ekraanile klassi isendi printimisel. Selleks oleks mõistlik defineerida ülemklassis Koer
meetod __str__
, mis pärandatakse kõikidesse alamklassidesse.
class Koer: def __init__(self, nimi, vanus, lemmiksöök): self.nimi = nimi self.vanus = vanus self.lemmiksöök = lemmiksöök def hääl(self): return "Auh Auh" def __str__(self): return f"{self.nimi} on {self.vanus} aastat vana ning tema lemmiksöök on {self.lemmiksöök}" carlos = KingCharlesCavalierSpaniel("Carlos", 4, "vorst") ralf = SaksaLambakoer("Ralf", 2, "kohupiim") print(carlos) print(ralf)
Carlos on 4 aastat vana ning tema lemmiksöök on vorst Ralf on 2 aastat vana ning tema lemmiksöök on kohupiim
Nüüd klassi isendit printides väljastatakse ekraanile isendi kohta loogiline info.
Selleks, et tagastada kogu info klassi isendi kohta ühe sõnena, on siin kasutatud Pythoni vormindatud sõnet (ingl formatted string). Selle puhul lisada muutujaid otse sõne sisse, ilma sõnet tükkideks jagamata. Muutujate lisamiseks sõnesse peame need ümbritsema looksulgudega.
Ülemklassi konstruktori väljakutsumine
Vahel on vaja alamklassis välja kutsuda hoopis ülemklassi konstruktorit. Teeme siinkohal sellise näite, et kõik saksa lambakoerad saavad automaatselt nimeks Rex. Sellisel juhul saame saksa lambakoera klassis kasutada ülemklassi Koer
konstruktorit käsklusega super()
:
class SaksaLambakoer(Koer): def __init__(self, nimi, vanus, lemmiksöök): super().__init__("Rex", vanus, lemmiksöök) def hääl(self): return "Vuff Vuff"
Sellisel juhul juhtub see, et hoolimata sellest, kui panime saksa lambakoerale nimeks näiteks Ralf, saab ta nimeks ikkagi Rex.
Video
Järgmises videos on selgitatud edasi objektorienteeritud programmeerimise põhimõtteid.