Arvutiteaduse instituut
  1. Kursused
  2. 2022/23 kevad
  3. Programmeerimise alused II (MTAT.03.256)
EN
Logi sisse

Programmeerimise alused II 2022/23 kevad

  • Kursuse info
  • 1. Kahemõõtmeline järjend
  • 2. Kahekordne tsükkel
  • 3. Andmestruktuurid
  • 4. Viitamine ja muteerimine
  • 5. Testimine ja silumine. Rekursioon
  • 6. Rekursioon II
  • 7. Objektorienteeritud programmeerimine
IV OSA sisukord

7 OBJEKT-ORIENTEERITUD PROGRAMMEERIMINE

Klassid kui tabelid

Varem oleme vaadanud, 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. Sellele märksõnale järgneb klassi nimi ja koolon. Klasside nimed kirjutatakse suure algustähega ja peavad olema unikaalsed. Klassi sisu kirjutatakse taandega.

class Õppija:
    # klassi sisu

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. Isendivälju saame kasutada selle märksõna kaudu.

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 (meetodite) esimese argumendina. Ühte levinud isendispetsiifilist funktsiooni vaatleme järgmisena.

Konstruktor

Konstruktor on klassile iseloomulik funktsioon, kus defineeritakse isendimuutujad. 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. Konstruktori sees omistatakse need väärtused vastavatele isendimuutujatele ehk isendiväljadele. Argumendi ja isendimuutuja nimi ei pea olema sama, aga võib olla.

class Õppija:
    def __init__(self, matrikli_nr, eesnimi, perenimi, isikukood):
        self.matrikli_nr = matrikli_nr
        self.eesnimi = eesnimi
        self.perenimi = perenimi
        self.isikukood = isikukood

Uue isendi loomine

Uue isendi loomisel soovime isendimuutujad väärtustada ehk omistada neile mingid väärtused. 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)
 Albert

print(õppur2.eesnimi)
 Pearu

Sarnaselt üksikute muutujate lugemisele võime isendimuutujaid ka ümber väärtustada või lisada.

õppur1.lemmikloom = "koer"
print(õppur1.lemmikloom)

Väljastatakse "koer".

Meetodid

Klassis saame luua meetodeid ehk isendispetsiifilisi funktsioone, mis määravad objekti käitumise. Nii võime sama meetodi 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"

Meetodite väljakutsumine on sarnane sõnede peal split ja strip meetodite kasutusega:

print(õppur1.sugu())
 M

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 meetod __str__:

def __str__(self):
    return "Õppija nimi: " + self.eesnimi + " " + self.perenimi + "."

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.


# Väljastamine, kasutades isendispetsiifilist funktsiooni koos staatilise muutuja ja funktsiooniga

print(õppur1)

 Õppija nimi: Albert Paas.
 Õpib koolis Tartu Ülikool.

print(õppur2)

 Õppija nimi: Pearu Murakas.
 Õpib koolis Tartu Ülikool.

Isendid andmestruktuurides

Tähele võib panna ka võimalust panna isendeid teistesse andmestruktuuridesse nagu järjend.


õppurid = [õppur1, õppur2]
for el in õppurid:
    print(el.eesnimi, el.perenimi + ", isikukood:", el.isikukood)
 Albert Paas, isikukood: 34105212737
 Pearu Murakas, isikukood: 34206122154

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.

Näidiskoodi lõppkuju:

class Õppija:
    kool = "Tartu Ülikool"
    def kusÕpib():
        return "Õpib koolis " + Õppija.kool + "."
    def __init__(self, matrikli_nr, eesnimi, perenimi, isikukood):
        self.matrikli_nr = matrikli_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))

# Isendid järjendis
õppurid = [õppur1, õppur2]
for el in õppurid:
    print(el.eesnimi, el.perenimi + ", isikukood:", el.isikukood)


IV OSA sisukord
  • Arvutiteaduse instituut
  • Loodus- ja täppisteaduste valdkond
  • Tartu Ülikool
Tehniliste probleemide või küsimuste korral kirjuta:

Kursuse sisu ja korralduslike küsimustega pöörduge kursuse korraldajate poole.
Õppematerjalide varalised autoriõigused kuuluvad Tartu Ülikoolile. Õppematerjalide kasutamine on lubatud autoriõiguse seaduses ettenähtud teose vaba kasutamise eesmärkidel ja tingimustel. Õppematerjalide kasutamisel on kasutaja kohustatud viitama õppematerjalide autorile.
Õppematerjalide kasutamine muudel eesmärkidel on lubatud ainult Tartu Ülikooli eelneval kirjalikul nõusolekul.
Courses’i keskkonna kasutustingimused