Arvutiteaduse instituut
  1. Kursused
  2. 2025/26 sügis
  3. Programmeerimine (LTAT.03.001)
EN
Logi sisse

Programmeerimine 2025/26 sügis

  • Üldinfo
  • 1. Muutuja ja avaldis
  • 2. Tingimuslause
  • 3. Funktsioon
  • 4. Korduslause
  • 5. Sõned. Lihtsam failitöötlus
  • 6. Kontrolltöö 1
  • 7. Järjend
  • 8. Järjend 2
  • 9. Kahekordne tsükkel. Failitöötlus
  • 10. Andmestruktuurid
  • 11. Andmestruktuurid 2
  • 12. Kontrolltöö 2
  • 13. Objektorienteeritud programmeerimine
  • 14. Objektorienteeritud programmeerimine 2
  • 15. Rekursioon
  • 16. Kordamine. Projektide esitlused
  • Viiteid
  • Silmaringimaterjalid

Arhiveeritud esialgsed silmaringimaterjalid

  1. Standardteek ja moodulid
  2. Rakendusliidesed
  3. Regulaaravaldised
  4. Andmebaasid
  5. Veebirakenduste loomine
  6. Objektorienteeritud programmeerimine
  7. Graafiliste mängude loomine
  8. Keerulisemad Pythoni võimalused
  9. Võistlusprogrammeerimine
  10. Veebisisu parsimine
  11. Pilditöötlus
  12. Pythoni siseehitus
  13. Helitöötlus
  14. Kodeerimine ja krüpteerimine
  15. NumPy
  • Materjalid

8. Keerulisemad Pythoni võimalused

Käesolev Pythoni kursus teeb hea sissejuhatuse Pythoniga programmeerimisse, aga puhtalt aine läbimisega ennast võluriks kutsuda ei saa. Selle jaoks peab läbima selle viimase silmaringimaterjalide peatüki.

Siin peatükis vaatame, kuidas teha lihtsaid asju keerulisemalt, et vähendada üleliigse koodi kirjutamist. Õpime keerulisemaid Pythoni võimalusi, et teha elu lihtsamaks.

Ettevalmistus

Selle peatüki läbimiseks piisab Pythoni installatsioonist: midagi paigaldama ei pea.

Et peatükist täiesti aru saada, peab olema läbitud õpiku esimene ja teine osa.

Lühem if-lause

Vahepeal on vaja millegi väga lihtsa jaoks kasutada if-lauset. Näiteks, sõna kääne muutub osastavaks, kui see viitab mitmele esemele: ostukorvis on 1 toode, aga ostukorvis võib olla mitu toodet.

Sellises olukorras peame kasutama if-lauset:

if toodete_arv == 1:
    print("Ostukorvis on {} toode".format(toodete_arv))
else:
    print("Ostukorvis on {} toodet".format(toodete_arv))

Et vähendada kirjutatud koodi, saab if-lause kirjutada ühele reale:

print("Ostukorvis on {} {}".format(toodete_arv, "toode" if toodete_arv == 1 else "toodet"))

Lühema if-lause valem:

väärtus_kui_tõene if tingimus else väärtus_kui_väär

Näited:

>>> "tõene" if True else "väär"
'tõene'
>>> "tõene" if False else "väär"
'väär'
>>> "paaris" if 20 % 2 == 0 else "paaritu"
'paaris'
>>> "paaris" if 21 % 2 == 0 else "paaritu"
'paaritu'
>>> "tühi" if len([]) == 0 else "täis"
'tühi'
>>> "tühi" if len([5]) == 0 else "täis"
'täis'

Proovi kirjutada lühike tingimuslause, mis annab väärtuseks "fizz", kui arv jagub kolmega ning arvu ise, kui see ei jagu kolmega.

Meil on järjend, mis sisaldab esemeid, mida on keelatud lennukile kaasa võtta keelatud.

keelatud = ["veepudel", "kahvel", "žilett", "ilutulestikud", "elevant"]

Kirjuta programm, mis küsib kasutajalt eset ning väljastab, kas see on keelatud või mitte nii, et programmil ei ole ühtegi taanet.

Sisesta ese: telefon
telefon ei ole keelatud lennukile kaasa võtmiseks
Sisesta ese: veepudel
veepudel on keelatud lennukile kaasa võtmiseks

Järjendi hõlmamine

Kui tahame luua uut järjendit ja täita seda mingite andmetega, oleme siiamaani loonud uue järjendi ning seejärel for-tsükliga sinna elemente juurde lisanud.

Näiteks, meil on järjend sõnadest ja me tahame saada järjendit nende pikkustest.

sõned = ["Python", "on", "üldotstarbeline", "interpreteeritav", "programmeerimiskeel"]

pikkused = []
for sõne in sõned:
        pikkused.append(len(sõne))

print(pikkused)  # [6, 2, 15, 16, 19]

Sama asja saab kirja panna vähema koodiga:

sõned = ["Python", "on", "üldotstarbeline", "interpreteeritav", "programmeerimiskeel"]

pikkused = [len(sõne) for sõne in sõned]

print(pikkused)  # [6, 2, 15, 16, 19]

Seda võtet nimetatakse järjendi hõlmamiseks (ingl. k. list comprehension).

Veel üks näide: tahame saada kahe astmeid. Pikemalt saab teha nii:

kahe_astmed = []
for arv in range(10):
        kahe_astmed.append(2**arv)

print(kahe_astmed)  # [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]

Järjendi hõlmamisega:

kahe_astmed = [2**arv for arv in range(10)]

print(kahe_astmed)  # [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]

Ülesanne võib olla natuke keerulisem. Näiteks soovime kahe astmeid ainult siis, kui need lõppevad neljaga.

kahe_astmed_neljaga = []

for arv in range(23):
    if 2**arv % 10 == 4:
        kahe_astmed_neljaga.append(2**arv)

print(kahe_astmed_neljaga)  # [4, 64, 1024, 16384, 262144, 4194304]

Ka lisatingimusi saame panna järjendi hõlmamisse:

kahe_astmed_neljaga = [2**arv for arv in range(23) if 2**arv % 10 == 4]

print(kahe_astmed_neljaga)  # [4, 64, 1024, 16384, 262144, 4194304]

Proovi järjendi hõlmamisega luua järjend, mis sisaldab ainult arve, mis ei jagu arvudega 3 ega 5:

[1, 2, 4, 7, 8, 11, 13, 14, 16, 17, 19, ...]

Meil on järjend, mis sisaldab kõige sagedasemaid sõnade põhivorme (lemmasid) koos nende järkudega:

[(1, "olema"), (2, "ja"), (3, "see"), (4, "tema"), (5, "mina"), (6, "ei"), (7, "et"), (8, "kui"), (9, "mis"), (10, "ka")]

Proovi järjendi hõlmamisega luua järjend, mis jätab alles ainult sõnad:

["olema", "ja", "see", "tema", "mina", "ei", "et", "kui", "mis", "ka"]

Proovi hõlmamist ka kuhjadega, sõnastikega ja ennikutega.

Funktsioonide kasutamine parameetrites

Sorteerimine

Kui me tahame sorteerida arvude järjendit, siis saab kasutada funktsiooni sorted või meetodit sort.

>>> arvud = [1, 5, 9, 2, 6, 5]
>>> sorted(arvud)  # ei sorteeri muutujat, tagastab sorteeritud järjendi
[1, 2, 5, 5, 6, 9]
>>> arvud
[1, 5, 9, 2, 6, 5]
>>> arvud.sort()  # sorteerib muutuja
>>> arvud
[1, 2, 5, 5, 6, 9]

Mis siis, kui meil on vaja midagi keerulisemat sorteerida? Näiteks meil on järjendisse salvestatud ennikud, mille üks element on arv. Kuidas öelda sorteerimisfunktsioonile, et sorteerida arvu järgi?

maletajad = [("Ding Liren", 3), ("Nepomnjaštši", 4), ("Caruana", 2), ("Carlsen", 1)]

Sorteerimisfunktsioonidel on parameeter key, millele saab panna väärtuseks funktsioone. See funktsioon jooksutatakse iga järjendi elemendi peal läbi nii, et parameetriks pannakse element ning järjend sorteeritakse selle funktsiooni tagastuste põhjal. Meie järjendi puhul saaksime teha funktsiooni, mis tagastab enniku teise liikme.

def teine_liige(ennik):
        return ennik[1]

Nüüd peab meelde tuletama, et funktsioonid on ka muutujad, lihtsalt neid saab välja kutsuda. Seega meie funktsioon on salvestatud muutujasse teine_liige. Sorteerime järjendi nii, et key parameetri väärtuseks läheb see funktsioon.

>>> maletajad
[('Ding Liren', 3), ('Nepomniachtchi', 4), ('Caruana', 2), ('Carlsen', 1)]
>>> maletajad.sort(key=teine_liige)
>>> maletajad
[('Carlsen', 1), ('Caruana', 2), ('Ding Liren', 3), ('Nepomniachtchi', 4)]

Töötas! Proovime veel. Kui meil on sõnede järjend, sorteeritakse neid tavaliselt tähestiku järjekorras:

>>> sõned = ["aaaa", "b", "ccc", "ddddd", "ee"]
>>> sorted(sõned)
['aaaa', 'b', 'ccc', 'ddddd', 'ee']

Aga mis siis, kui tahame neid sorteerida sõnepikkuse järjekorras? Peaksime tegema funktsiooni, mis tagastab sõne pikkuse.

def sõne_pikkus(sõne):
        return len(sõne)

Oota, selline funktsioon on juba olemas ja seda kasutasime isegi siin funktsioonis: len. Paneme sorteerimisel parameetri key väärtuseks lihtsalt selle!

>>> sorted(sõned, key=len)
['b', 'ee', 'ccc', 'aaaa', 'ddddd']

Saime isegi meeldiva ja loetava koodi.

Kui funktsiooni pole veel kirjutatud, peame selle ise defineerima, nagu tegime esimeses näites. Üsna tüütu on nii lihtsat funktsiooni ise defineerida. Võiks ju olla lihtsam viis funktsiooni defineerimiseks ilma, et peab mitu rida koodi juurde kirjutama.

Lambda-funktsioonid

Et vähendada koodi kirjutamist, saame kasutada lambda-funktsioone. Need on funktsioonid, mida saab kirjutada lühidalt ja kasutada anonüümselt ehk neid ei pea muutujasse salvestama.

Vaatame funktsiooni, mida me enne kirjutasime:

def teine_liige(ennik):
        return ennik[1]

Seda saab samaväärselt kirjutada lambdana:

teine_liige = lambda ennik: ennik[1]

Defineerisime just funktsiooni teine_liige, mis võtab parameetriks muutuja ennik ning tagastab selle teise liikme.

Saame neid samamoodi kasutada teiste funktsioonide parameetrites:

>>> maletajad
[('Ding Liren', 3), ('Nepomniachtchi', 4), ('Caruana', 2), ('Carlsen', 1)]
>>> maletajad.sort(key=lambda ennik: ennik[1])
>>> maletajad
[('Carlsen', 1), ('Caruana', 2), ('Ding Liren', 3), ('Nepomniachtchi', 4)]

Tegelikult on sellised funktsioonid ka juba Pythoni standardteegis olemas, aga nende kasutamiseks peab ühe mooduli importima ning nendega sai teha hea sissejuhatuse lambdadesse.

Vaatame veel funktsioone, mis nõuavad parameetritena teisi funktsioone.

Funktsioon map

Üks asi, mis võib tihti ette tulla, on kogu järjendi elementidele mingi funktsiooni rakendamine. Näiteks on vaja arvusõnede järjend muuta arvude järjendiks. Selle jaoks peaks need for-tsükliga läbi käima ja kuskile uude järjendisse lisama.

sõned = ["3", "1", "4", "1", "5", "9"]

arvud = []
for sõne in sõned:
        arvud.append(int(sõne))

print(arvud)  # [3, 1, 4, 1, 5, 9]

See on päris palju koodi nii lihtsa asja jaoks. Et säästa koodi kirjutamist, on Pythonis funktsioon map. Sellele saab parameetriteks panna ühe funktsiooni ning järjendi, et jooksutada seda funktsiooni kõikide järjendi elementide peal. See tagastab objekti, mida saab läbida for-tsükliga või muuta järjendiks funktsiooniga list. Eelmine kood uuesti, kasutades funktsiooni map:

sõned = ["3", "1", "4", "1", "5", "9"]

arvud = list(map(int, sõned))

print(arvud)  # [3, 1, 4, 1, 5, 9]

Tegime palju lühema koodiga sama asja.

Võimalik, et funktsioon nõuab mitut parameetrit, näiteks round, mille esimene parameeter on ümardatav arv ja teine on kohtade arv, mitmeni peaks ümardama.

>>> round(3.14159265, 2)
3.14

Sel juhul saab map parameetriteks panna 2 järjendit: esimesed parameetrid ja teised parameetrid. Ümardame näiteks ujukomaarvude järjendi arvud vastavalt teisele järjendile.

arvud = [3.14159, 2.71828, 1.41421, 6.28318, 1.61803]
kohtadeni = [2, 3, 4, 3, 2]

ümardatud = list(map(round, arvud, kohtadeni))
print(ümardatud)  # [3.14, 2.718, 1.4142, 6.283, 1.62]

Proovi map funktsiooniga kõik järjendi sõned suurtähestada (sõne meetod upper).

Funktsioon filter

Vahepeal on vaja järjendeid filtreerida. Näiteks tahame järjendist jätta alles ainult positiivsed arvud. Võib jälle teha for-tsükli:

arvud = [3, -6, 1, -2, 4, -8, 1, -3]
positiivsed = []

for arv in arvud:
    if arv > 0:
        positiivsed.append(arv)

print(positiivsed)  # [3, 1, 4, 1]

Seda saab lühemalt teha funktsiooniga filter. See funktsioon nõuab parameetritesse funktsiooni ning järjendit. Tagastatud järjendisse jäetakse alles ainult need elemendid, mille peal funktsioon tagastab True. Eelmine koodijupp funktsiooniga filter:  

arvud = [3, -6, 1, -2, 4, -8, 1, -3]
            
positiivsed = list(filter(lambda arv: arv > 0, arvud))

print(positiivsed)  # [3, 1, 4, 1]

Seda saab ka kasutada sõnede peal. Näiteks saame alles jätta kõik tähed ja tühikud tekstis ilma muude sümboliteta:

lause = "„Funktsionaalprogrammeerimine on lihtne,” ütles mitte keegi."

print("".join(filter(lambda x: x.isalpha() or x == " ", lause)))
# Funktsionaalprogrammeerimine on lihtne ütles mitte keegi

Kuna filter tagastab järjendilaadse objekti, peab sõne saamiseks selle kokku liitma sõne meetodiga join.

Kasuta filter funktsiooni, et saada kätte salajane sõnum järgnevast tekstist:

XXXSXXXXXXAXXXXXXLXXXXXXAXXXXXXJXXXXXXAXXXXXXNXXXXXXEXXXXXX XXXXXXSXXXXXXÕXXXXXXNXXXXXXUXXXXXXMXXX

Veel üks huvitav funktsioon on reduce.

Funktsioon zip

Kui meil on mitu järjendit sarnaste andmetega ja me tahame neid kokku pakkida, siis peaksime need for-tsükliga indekseerima:

eesnimed = ["Jüri", "Mailis", "Tõnis", "Tanel", "Urmas"]
perenimed = ["Ratas", "Reps", "Lukas", "Kiik", "Reinsalu"]

ministrid = []

for i in range(len(eesnimed)):
        ministrid.append((eesnimed[i], perenimed[i]))

print(ministrid)
# [('Jüri', 'Ratas'), ('Mailis', 'Reps'), ('Tõnis', 'Lukas'), ('Tanel', 'Kiik'), ('Urmas', 'Reinsalu')]

Funktsioon zip lubab seda palju lihtsamalt teha:

eesnimed = ["Jüri", "Mailis", "Tõnis", "Tanel", "Urmas"]
perenimed = ["Ratas", "Reps", "Lukas", "Kiik", "Reinsalu"]

print(list(zip(eesnimed, perenimed)))
# [('Jüri', 'Ratas'), ('Mailis', 'Reps'), ('Tõnis', 'Lukas'), ('Tanel', 'Kiik'), ('Urmas', 'Reinsalu')]

See on kasulik, kui meil on näiteks erinevaid andmeid samade asjade kohta ja me tahame neid kokku liita.

Funktsioonile zip võib sisse sööta lõpmatu arvu parameetreid:

>>> list(zip([1], [2], [3], [4], [5]))
[(1, 2, 3, 4, 5)]

Seega, kui me sisestame saadud paarid zip funktsiooni parameetritesse, siis saame tagasi eesnimede ja perenimede järjendid:

>>> list(zip(('Jüri', 'Ratas'), ('Mailis', 'Reps'), ('Tõnis', 'Lukas'), ('Tanel', 'Kiik'), ('Urmas', 'Reinsalu')))
[('Jüri', 'Mailis', 'Tõnis', 'Tanel', 'Urmas'), ('Ratas', 'Reps', 'Lukas', 'Kiik', 'Reinsalu')]

Aga kui meil on paarid järjendi sees, siis kuidas kõiki elemente funktsiooni parameetritesse saada?

Et järjendit funktsiooni parameetritena kasutada, tuleb järjend panna parameetriks ning selle ette kirjutada tärn. Proovime algul lihtsama näitega: paneme funktsiooni round parameetriks kaheliikmelise järjendi.

>>> round(*[3.14159265368, 2])
3.14

Proovime sama võtet zip funktsiooni peal:

>>> ministrid = [('Jüri', 'Ratas'), ('Mailis', 'Reps'), ('Tõnis', 'Lukas'), ('Tanel', 'Kiik'), ('Urmas', 'Reinsalu')]
>>> list(zip(*ministrid))
[('Jüri', 'Mailis', 'Tõnis', 'Tanel', 'Urmas'), ('Ratas', 'Reps', 'Lukas', 'Kiik', 'Reinsalu')]

Töötab! Oleme edukalt järjendid kokku ja lahti pakkinud.

Kokkuvõte

Kasutasime erinevaid võtteid, et vähendada üleliigset koodi kirjutamist. Võibolla tundub tõesti, et see teeb lihtsaid asju keerulisemaks, aga piisava harjutamisega muutuvad need lihtsamaks ning pikema koodi kirjutamise soov kaob ära.

Kuigi õpitud võtted lubavad lahendada peaaegu kõiki ülesandeid ühe reaga, peab meelde tuletama, et Pythoni ametlik stiiliõpetus nõuab, et read on maksimaalselt 79 tähemärgi pikkused. Päris programmides tuleks kood mõistlikult paigutada mitmele reale.

Paljud siin peatükis rakendatud funktsioonid on seotud funktsionaalprogrammeerimisega. Sellesse teemasse süveneb aine "Programmeerimiskeeled" (MTAT.03.006) keeltega Haskell ja Scala.

Enesekontrolliküsimused

Ülesanded

1. Antud on järjendid nimedest, matriklinumbritest ja keskmistest hinnetest.

nimed = ["Jaan", "Martin", "Katrin", "Margus", "Tiiu", "Jüri", "Anna", "Sirje", "Ülle", "Kristjan", "Anne", "Julia", "Andres", "Marina", "Rein", "Aivar", "Tiina", "Urmas", "Toomas", "Maria"]
matriklinumbrid = ["B69310", "B28761", "B79826", "B14207", "B16122", "B61619", "B14708", "B59695", "B50264", "B32270", "B88961", "B73302", "B29125", "B87856", "B48386", "B22124", "B52814", "B80444", "B56290", "B57742"]
hinded = [4.15, 3.61, 3.59, 3.28, 4.5, 4.6, 4.16, 3.83, 4.97, 4.26, 4.92, 3.93, 3.64, 4.12, 4.03, 4.75, 4.38, 4.65, 3.09, 4.04]

Kirjuta ühe reaga funktsioon, mis tagastab ennikud nendest, kelle keskmine hinne on vähemalt 4.6, sorteeritud keskmise hinde järgi kahanevalt.

>>> stipisaajad(nimed, matriklinumbrid, hinded)
[('Ülle', 'B50264', 4.97), ('Anne', 'B88961', 4.92), ('Aivar', 'B22124', 4.75), ('Urmas', 'B80444', 4.65), ('Jüri', 'B61619', 4.6)]

2. Tekstifaili evolutsioon.txt on salvestatud tekst imelikul kujul: tekst algab ülevalt paremalt ja liigub alla. Kirjuta ühe reaga funktsioon korrasta, mis võtab parameetrina failinime ning tagastab õiges järjekorras loetud teksti. Lahendamiseks piisab siin peatükis kasutatud funktsioonidest, v.a faili sisselugemine. Faili sulgemine siin ülesandes pole tähtis.

nne mE
g,raon
, fnsd
 audtl
enl  e
vd mbs
o hoes
laasa
vrvtuf
eee to
d  wir
.bbofm
 eenus
 iedl
>>> with open("ül1.py") as f: print(len(f.readlines()))
1
>>> %Run 'ül1.py'
>>> korrasta("evolutsioon.txt")
'Endless forms most beautiful and most wonderful have been, and are being, evolved.  '
  • 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