IV OSA sisukord |
4.5 OBJEKT-ORIENTEERITUD PROGRAMMEERIMINE
Klassid kui tabelid
Eelmisel nädalal vaatasime, kuidas andmeid hoitakse andmebaasides ning kuidas neid andmeid andmebaasist kätte saada. Sellel nädalal vaatame, kuidas andmeid saab hoida objektides.
Objektis informatsiooni hoidmiseks peame läbi mõtlema, kuidas soovime objektis andmeid kirjeldada. Sarnaste objektide jaoks ühiste reeglite sätestamiseks kasutame klasse. Klassid on nagu andmebaaside tabelid, mis ütlevad, millist infot me iga tabelis oleva isendi kohta teame. Klassi defineerimiseks kasutatakse märksõna class
.
Meenutame Õppija
tabelit: igal Õppijal
on enda matriklinumber
, eesnimi
, perekonnanimi
ja isikukood
. Need neli tunnust võime lugeda muutujateks, mida väärtustame iga Õppija
jaoks eraldi.
Muutujaid, mis kehtivad iga isendi enda kohta, nimetatakse isendimuutujateks ehk isendiväljadeks.
Pythonis kasutatakse nende klassisiseseks eristamiseks märksõna self
.
Nõnda saame klassis Õppija
defineerida muutujad self.matrikli_nr
, self.eesnimi
, self.perenimi
ja self.isikukood
. Klassist väljaspool nende muutujate kasutamisel kasutatakse self
märksõna asemel vastavat isendit hoidva muutuja nime. self
märksõna kasutatakse ka isendi informatsiooni kasutavate klassis defineeritud funktsioonide esimese argumendina. Ühte levinud isendispetsiifilist funktsiooni vaatleme järgmisena.
Uue isendi loomine
Uue isendi loomisel soovime need isendimuutujad väärtustada ehk omistada neile mingid väärtused. Selleks kasutatakse konstruktorit, mis on klassile iseloomulik funktsioon. Konstruktorit kasutatakse iga isendi loomisel isendimuutujate väärtustamiseks. Funktsiooni nimeks on __init__
, mis tuleneb uue isendi initsialiseerimisest ehk lähtestamisest. Funktsiooni parameetriteks on esmalt märksõna self
ning seejärel kõik teised muutujad, mida soovime isendisse salvestada.
class Õppija: def __init__(self, mat_nr, eesnimi, perenimi, isikukood): self.matrikli_nr = mat_nr self.eesnimi = eesnimi self.perenimi = perenimi self.isikukood = isikukood
Märkame, et self.eesnimi
muutuja on isendispetsiifiline, kuid eesnimi muutuja pärineb funktsiooni parameetritest. Kui kasutada sama nimega muutujaid, tuleb olla eriti tähelepanelik, millist muutujat me igal hetkel kasutame. Kui kogemata muutujad segamini ajame, võib meie loodav programm käituda ettearvamatult. Loodud konstruktorit saame kasutada uue isendi loomisel. Kui konstruktori __init__
defineerimine oli nagu andmebaaside CREATE TABLE
käsk, siis konstruktori kasutamine on analoogne INSERT INTO
käsuga:
õppur1 = Õppija("A034", "Albert", "Paas", 34105212737) õppur2 = Õppija("A037", "Pearu", "Murakas", 34206122154) print(õppur1.eesnimi) print(õppur2.eesnimi)
Sarnaselt üksikute muutujate lugemisele võime isendimuutujaid ka ümber väärtustada või lisada.
õppur1.lemmikloom = "koer" print(õppur1.lemmikloom)
Väljastatakse "koer"
.
Isendispetsiifilised funktsioonid
Kui soovime välja printida informatsiooni isendi kohta, siis üheks variandiks on üksikute isendiväljade kasutamine. print(õppur1)
annab aga hetkel eriskummalise tulemuse
<__main__.Õppija object at 0x02BFBE90>
, mis ütleb, et tegu on isendiga klassist Õppija
ning see asub mäluväljal 0x02BFBE90
. Selleks, et print
käsk annaks meile informatiivsema kirjelduse, tuleb klassis defineerida isendispetsiifiline funktsioon __str__
:
def __str__(self): return "Õppija nimi: "+self.eesnimi+" "+self.perenimi+"."
Kui soovime, võime isendispetsiifilisi funktsioone juurde teha. Sellisel juhul võime sama funktsiooni välja kutsuda mistahes Õppija
isendi peal, saades isendi andmetest sõltuva tulemuse.
def sugu(self): if self.isikukood//10000000000%2 == 1: #Vaatleme, kas isikukoodi esimene number on paaris- või paaritu arv. return "M" else: return "N"
Funktsioonide väljakutsumine on sarnane sõnede peal split
ja strip
funktsioonide kasutusega:
print(õppur1.sugu())
Staatilised muutujad ja funktsioonid
Lisaks isendispetsiifilistele muutujatele ja funktsioonidele võib klassis defineerida ka kõikidele sama klassi isenditele ühiseid muutujaid ja funktsioone. Neid nimetatakse staatilisteks, kuna nad ei muutu sõltuvalt isendist. Staatilisi muutujaid võib võrrelda globaalsete muutujatega, mispuhul isendispetsiifilised muutujad on sarnased lokaalsete muutujatega - kasutatakse funktsioonides.
Sarnaselt globaalsete muutujatega deklareeritakse staatilised muutujad klassi sees funktsiooniväliselt. Erinevalt isendispetsiifiliste muutujatest tuleb staatiliste muutujate lugemiseks või muutmiseks pöörduda isendi asemel terve klassi poole. Kui meie näites oleks eesnimi staatiline muutuja, tuleks õppur1.eesnimi
asemel kasutada Õppija.eesnimi
. Seejuures saab, kuid ei ole soovitatav, kasutada sama nimega staatilisi ja isendispetsiifilisi muutujaid.
Staatilised funktsioonid erinevad isendispetsiifilistest vaid paari omaduse poolest: parameetrites ei kasutata märksõna self
, funktsioonis sees ei saa mittestaatilisi isendimuutujaid kasutada ning funktsiooni väljakutsumiseks kasutatakse isendi muutujanime asemel klassi üldnimetust.
Võime lisada klassi algusesse ühe muutuja kool
, ühe staatilise funktsiooni ning muuta väljaprinditavat infot:
class Õppija: kool = "Tartu Ülikool" def kusÕpib(): return "Õpib koolis "+Õppija.kool+"." def __str__(self): return "Õppija nimi: "+self.eesnimi+" "+self.perenimi+".\n"+Õppija.kusÕpib()
Nüüd on huvitav uurida print(õppur1)
ja print(õppur2)
väljundit.
Lõpetuseks
Oleme vaadelnud, kuidas ehitada objekt erinevate omadustega. Objekte on hea kasutada olukordades, kus ühe ja sama väärtusega on seotud hästi palju teisi. Õppija
klassi asemel oleksime saanud kasutada ka näiteks kolme sõnastikku, kus ühes on matriklinumbrile vastav eesnimi, teises perekonnanimi, kolmandas isikukood. Ühendades kõik ühte isendisse muutub kood palju kergemini loetavaks ja hallatavamaks. Tähele võib panna ka võimalust panna isendeid teistesse andmestruktuuridesse nagu järjend, mispuhul ei ole vaja iga uue isendi jaoks uut muutujanime, piisab järjendi nimest ja indeksist. Sellisel juhul saab isendeid tekitada tsüklitega.
Näidiskoodi lõppkuju:
class Õppija: kool = "Tartu Ülikool" def kusÕpib(): return "Õpib koolis "+Õppija.kool+"." def __init__(self, mat_nr, eesnimi, perenimi, isikukood): self.matrikli_nr = mat_nr self.eesnimi = eesnimi self.perenimi = perenimi self.isikukood = isikukood def __str__(self): return "Õppija nimi: "+self.eesnimi+" "+self.perenimi+".\n"+Õppija.kusÕpib() def arvuta(a,b): return a+b def sugu(self): if self.isikukood//10000000000%2 == 1: return "M" else: return "N" #Konstruktori kasutus õppur1 = Õppija("A034", "Albert", "Paas", 34105212737) õppur2 = Õppija("A037", "Pearu", "Murakas", 34206122154) #Isendimuutuja tekitamine/muutmine õppur1.lemmikloom = "koer" print(õppur1.lemmikloom) #Isendispetsiifiline funktsioon print(õppur1.sugu()) #Väljastamine, kasutades isendispetsiifilist funktsiooni koos staatilise muutuja ja funktsiooniga print(õppur1) print(õppur2) #Muu staatilise funktsiooni kasutus print(Õppija.arvuta(1,2))
IV OSA sisukord |