14. Objektorienteeritud programmeerimine
Seni oleme programme kirjutades salvestanud infot muutujatesse. Muutujasse saab salvestada eri andmetüüpe: täisarv, sõne, järjend, sõnastik jne. Kuid seni ei ole me saanud muutujatesse salvestada andmetüüpe, mida on võimalik ise defineerida. Selles peatükis tutvume klasside ja objektidega ning vaatame, kuidas neid luua ja kasutada. Lisaks uurime, kuidas saab klassidesse lisada meetodeid ja ära kasutada objektidele antud omadusi.
Sissejuhatus
Objektorienteeritud programmeerimise puhul on kõige olulisem objektide loomine. Objekt on andmetüüp, mis koosneb muutujatest ja meetoditest. Muutujad defineerivad objekti olemuse ning meetodid objekti käitumise. Objektide abil on programmid selgemad ja struktureeritumad.
Objekti näiteks võib võtta tavalise inimese. Inimese muutujaid, ehk olemust, võime kirjeldada sõnadega nagu pikkus, juuksevärv, sugu jne. Meetodite, ehk käitumise, alla sobivad tegevused nagu söömine, õppimine, magamine, trenni tegemine jne.
Selleks, et objekte kasutada, on vaja koostada klass. Klassis saab defineerida objekti meetodid ning objekti muutujad, mis paiknevad konstruktoris.
Klassi konstruktor
Klass on šabloon (ingl template), kus kirjeldatakse andmed ja meetodid, millega andmeid manipuleeritakse. Klasside loomine algab võtmeseõnaga class
, millele järgneb klassi nimi (suure algustähega) ja koolon. Ühes failis võib defineerida mitu erinevat klassi: iga klass peab algama võtmesõnaga class
ja olema unikaalse nimega.
Klassi konstruktoris (ingl constructor) defineeritakse objekti muutujad ja see käivitatakse ainult isendi (ingl instance) loomisel. Konstruktori nimi on igas klassis alati __init__
. Kõik muutujad, mis klassi isendil olemas on, tuleb defineerida konstruktoris. Neid muutujaid nimetatakse ka isendimuutujateks (ingl instance variables). Iga klassi isendi (objekti) loomisel pöördutakse meetodi __init__
poole, mis määrab objekti algoleku ja objekti seisundi (muutujate väärtused). Klassi iga uus objekt objekt initsialiseeritakse meetodi __init__
abil.
Näiteks loome klassi Tudeng
, mille isenditel on omadused nagu nimi, vanus, ülikooli eriala ja keskmine hinne.
class Tudeng: # Konstruktor def __init__(self, nimi, vanus, eriala, keskmine_hinne): # Tudengitüüpi objektidel on neli isendimuutujat. self.nimi = nimi self.vanus = vanus self.eriala = eriala self.keskmine_hinne = keskmine_hinne
Konstruktori abil anname tudengi klassi isenditele neli omadust: nime, vanuse, eriala ja keskmise hinde. Nüüd loome samas programmis kaks klassi Tudeng
isendit, millel on erinevad omadused. Tudengite muutujad peab defineerima klassi Tudeng
kehast väljas.
# klassi Tudeng lõpp tudeng1 = Tudeng("Karl Tamm", 21, "Bioloogia", 3.9) tudeng2 = Tudeng("Maria Hein", 24, "Informaatika", 4.6) print(tudeng1.vanus) print(tudeng2.eriala)
21 Informaatika
Nagu näha, on kahel tudengil erinevad omadused. Omadused ehk isendimuutujad saab kätte järgneva süntaksiga: objekti_nimi.isendimuutuja_nimi. Sarnaselt väljastatakse ülaltoodud näites esimese tudengi vanus ja teise tudengi õpitav eriala.
Parameeter self
Klassi konstruktorile (meetodile __init__
) võib anda kuitahes palju parameetreid, aga esimene parameeter peab alati olema self
. Uue klassi isendi loomisel edastatakse klassi isend automaatselt meetodi __init__
parameetrile self
. Selle muutuja väärtus viitab uuele loodud objektile. Seetõttu on kõik isendimuutujad seotud just selle loodud isendiga. Klassi konstruktori ja meetodite esimene parameeter peab alati olema self
.
Enesekontroll
Klassi meetodid
Klassis defineeritud meetodid on funktsioonid, mis on defineeritud klassi sees, ja neid saab välja kutsuda ainult klassi isendi (objekti) abil. Kõikide meetodide esimene parameeter on alati self
sarnaselt klassi konstruktoriga ehk meetodiga __init__
.
Täiendame klassi Tudeng
kahe meetodiga.
class Tudeng: # Klassi konstruktor def __init__(self, nimi, vanus, eriala, keskmine_hinne): # Tudengitüüpi objektidel on neli isendimuutujat self.nimi = nimi self.vanus = vanus self.eriala = eriala self.keskmine_hinne = keskmine_hinne # Meetod nr. 1 def instituut(self): if self.eriala.lower() == "bioloogia": return "Tudeng " + self.nimi + " õpib bio- ja siirdmeditsiini instituudis." elif self.eriala.lower() == "informaatika": return "Tudeng " + self.nimi + " õpib arvutiteaduse instituudis." else: return "Ei tunne teisi instituute." # Meetod nr. 2 def stipendium(self, stipendiumi_alampiir): if self.keskmine_hinne < stipendiumi_alampiir: return "Tudeng " + self.nimi + " ei saa stipendiumit." else: return "Tudeng " + self.nimi + " saab stipendiumit."
- Meetodil
instituut
ei ole ühtegi parameetrit (loomulikult peale parameetriself
, mis peab olema igal klassis sisalduvas meetodil), meetod tagastab sõltuvalt klassi isendi erialast instituudi, kus tudeng õpib. - Meetodi
stipendium
parameetriks on stipendiumi hindeline alampiir ujukomaarvuna, meetod tagastab sõnena, kas tudeng saab stipendiumit või mitte.
Mõlema meetodi puhul märkame, et meetodi kehas on pöördutud isendimuutujate poole võtmesõnaga self
(self.eriala, self.keskmine_hinne). Sellisel moel on võimalik meetodi väljakutsel kasutada objekti isendimuutujaid. Nagu eelpool mainitud, on meetodeid võimalik välja kutsuda ainult objekti abil, selle tõttu saame meetodite kehas pöörduda objekti enda isendimuutujate poole.
Veel konstruktori loomisest
Konstruktori loomisel võib lisada isendimuutujatele ka vaikeväärtuse või näiteks andmetüübi. Näiteks kui soovime, et kõik üliõpilased oleks Tartu Ülikooli üliõpilased, siis võime konstruktoris luua muutuja ülikool selliselt:
self.ülikool = "Tartu Ülikool"
Kui soovime luua isendimuutujat, mis oleks järjend, siis saab luua ka tühja järjendi, mida saab isendimeetodis muuta. Näiteks kui tahame üliõpilasele lisada järjendi ainete kohta, mida ta võtab, siis saame konstruktoris luua isendimuutuja
self.ained = []
Terve konstruktor näeks siis välja selline:
# Klassi konstruktor def __init__(self, nimi, vanus, eriala, keskmine_hinne): # Tudengitüüpi objektidel oleks siis kuus isendimuutujat self.nimi = nimi self.vanus = vanus self.eriala = eriala self.keskmine_hinne = keskmine_hinne self.ülikool = "Tartu Ülikool" self.ained = []
Ainete lisamiseks üliõpilase isendile võiks luua isendimeetodi, mis vastavasse järjendisse liikmeid juurde lisaks:
# Meetod nr. 3 def lisa_aine(self, aine): self.ained.append(aine)
Ja kasutada saab seda isendimeetodit selliselt:
tudeng1.lisa_aine("Programmeerimine") print(tudeng1.ained)
Objektide järjendisse lisamine
Objekte on võimalik lisada ka järjenditesse. Lisame järjendisse kaks klassi Tudengi isendit ja katsetame klassi meetodite tööd:
tudeng1 = Tudeng("Karl Tamm", 21, "Bioloogia", 3.9) tudeng2 = Tudeng("Maria Hein", 24, "Informaatika", 4.6) tudengid = [] tudengid.append(tudeng1) tudengid.append(tudeng2) print(tudengid[0].instituut()) print(tudengid[1].stipendium(4.6))
Tudeng Karl Tamm õpib bio- ja siirdmeditsiini instituudis. Tudeng Maria Hein saab stipendiumit.
Video
Järgmises videos on selgitatud objektorienteeritud programmeerimise põhimõtteid.