Funktsioonid Pythonis

From Wikiversity
Jump to navigation Jump to search

< 10. nädala teemad

Pythonis ei eristata otseselt protseduuri ja funktsiooni kirjeldust. Funktsioon tagastab traditsiooniliselt ühe väärtuse pärast andmete töötlemist (seda saab funktsiooni sees käsuga organiseerida). Kui funktsioonist ei tagastata väärtust, siis ongi tegemist sisuliselt protseduuriga.

Funktsiooni kirjeldamine[edit]

Funktsiooni kirjeldamiseks on def-lause:

def funktsiooni_nimi(parameetrite loetelu):
   "Funktsioooni kirjeldus inimkeeles"
   Funktsiooni sisu

Selgitused:

def – keele sõna, mis alustab funktsiooni kirjeldust

funktsiooni_nimi – programmeerija valitud nimi, mille kaudu funktsiooni hiljem välja kutsutakse (ehk käivitatakse); seega peaks nimi olema mõistlikult ja funktsiooni sisule vastavalt valitud

parameetrite loetelu – algandmed, mida funktsioon töötlema hakkab ja mis talle väljakutse kohast ette antakse (nt arv, mida juurima hakata jms)

Eelnev kolmik moodustab funktsiooni päise (header).

"kirjeldus ..." - tekst, mille abil kirjutatakse üles funktsiooni olulised omadused ja tegevused ehk mille jaoks see funktsioon kasulik on, mida ta leiab; see on väga soovitatav lisada - nendest saab automaatselt dokumentatsiooni genereerida (ingl docstring)

funktsiooni sisu – kood, mis tegelikult funktsiooni tegevuse määrab (nt programmi kood, mille abil arvust tema juur arvutatakse); kogu funktsiooni sisu peab olema kirjutatud taandega (ja võib sisaldada järgmisi taandeid, kui keele laused seda nõuavad).

Lisaks saab funktsioonis kasutada return-lauset, mille abil tagastatakse funktsioonist vastus (nt leitud ruutjuur):

return tulemus

Võib kirjutada ka ainult return, sel juhul tagastatakse tühi väärtus None. Peale return-lauset olevat koodi ei täideta kunagi, sest lisaks vastuse tagastamisele on see ka käsk funktsiooni töö lõpetamiseks.

Reeglina peavad funktsioonid olema enne kirjeldatud, kui neid kasutama hakata saab. Seega paiknevad funktsioonide kirjeldused programmi alguses (funktsioone võib seal mitu tükki olla). Kui üks funktsioon teist funktsiooni väljakutsuma peab, siis tuleb ka nende omavahelist järgnevust arvestada: väljakutsutav peab olema kirjeldatud enne väljakutsujat. St kui hakata programmi koodi algusest lugema, siis jõudes funktsiooni väljakutseni peaks selge olema, mida ja kuidas see funktsioon teeb.

Funktsiooni väljakutsumisel luuakse uus sümbolite tabel (peamiselt muutujate nimede jaoks). Sel teel tekivad funktsiooni lokaalsed muutujad. Samasse tabelisse satuvad ka funktsiooni parameetrid. Sama funktsioon uuel väljakutsel luuakse uus tabel. Sellest tulenevalt on funktsiooni sees tekkivad muutujad vaid selle funktsiooni omad (nagu ka üldine lokaalsete / globaalsete muutujate teooria jutustab). Funktsioon võib küll kasutada peaprogrammi muutujaid, kuid nende väärtuseid ta muuta ei saa (täpsustuseks – võib proovida, kuid see ei mõju, sest luuakse lihtsalt uus funktsiooni lokaalne muutuja).

Funktsiooni saab ka teise funktsiooni sees kirjeldada, kuid sel juhul on ta kättesaadav ja väljakutsutav ka vaid selle sama funktsiooni seest.

Funktsiooni parameetrid[edit]

Parameetrite põhiülesanne on lähteandmete toomine funktsiooni. Kõige tavalisem parameetrite edastamise viis on nö järjekorra alusel (positional arguments) – funktsiooni kirjelduses ja funktsiooni väljakutses on formaalsed ja tegelikud parameetrid samas järjekorras. Sel juhul peab formaalsete ja tegelike parameetrite arv olema vastavuses.


Pythonis võib parameetrite arv olla ka muutuv. Ja seetõttu on veel mõned võimalused nende edastamiseks.

Parameetritele saab määrata vaikeväärtused (default arguments). Seda tehakse funktsiooni kirjelduses omistusmärgi abil:

def astenda(arv, aste = 2):

See tähendab, et kui teist parameetrit (aste) funktsiooni väljakutses ei määrata, siis on astme väärtuseks 2. Väljakutse võib olla järgmine:

astenda(a1) 	# a1 tõstetakse ruutu
astenda(a2,3) 	# a2 tõstetakse kuupi

Kohustuslikud parameetrid tuleb panna esimesteks ja vaikeväärtustega parameetrid viimasteks. Vaikeväärtuste kasutamine võib hõlbustada programmeerija tööd. Üks näide nende kasutamisest on mitmesuguste konstantsete väärtuste kasutamine – näiteks tulumaksu, käibemaksu ja tont-teab-veel-mis-maksu määrad. Kui funktsiooni kirjeldaja on need väärtused ära määranud, siis ei pea teine programmeerija nende arvude pärast oma pead vaevama, kui just väga vaja ei ole. Alternatiiviks on vastavate konstantide määramine funktsiooni sees.

Parameetreid võib käsitleda ka kui võtmesõnu (keyword arguments) ja kasutada väljakutse lauses formaalse parameetri nime koos talle omistatava väärtusega (väärtuseks võib olla ka muutuja). Näiteks on lubatud järgmised funktsiooni väljakutsed (funktsiooni kirjeldus oli eespool):

astenda(arv = a1, aste = 2)
astenda(aste = 2, arv = a1)

NB! Formaalsel ja tegelikul parameetril ei pea olema sama nimi, sest sama funktsiooni peab saama rakendada erinevatele väärtustele. Võtmesõnade kasutamisest võib olla kasu, kui on vaja osa parameetreid vahele jätta või muidu ei suudeta tuvastada, millises järjekorras nad olema peavad. Teinekord võib see tõsta ka programmi loetavust – on näha, millisesse parameetrisse milline väärtus pannakse, sest paljude parameetrite korral hakkab järjekord segamini minema.

Järgnevalt väike näide:

# Funktsiooni algus
def kompliment (nimi):
    # Järgnev string on docstring (dokumentatsiooni genereerimiseks)
    "Funktsioon teeb komplimendi ilusa nime kohta"
    print("Tere ", nimi)
    print("Sul on väga armas nimi!")
# Funktsiooni kompliment() lõpp

# Peaprogrammi algus
print("Aga siit algab tegelikult programmi täitmine!")
eesnimi = input("Mis Su eesnimi on? ")
# Funktsioon kutsutakse välja kaks korda erinevate parameetritega
kompliment(eesnimi)
perenimi = input("Ja perekonnanimi? ")
kompliment(perenimi)
print("Oli tore Sinuga kohtuda!")
# Peaprogrammi lõpp

Eelneva programmi täitmise loogika on järgmine (proovi näide ka ise läbi):

  • Alustatakse eesnime küsimisest.
  • Seejärel kutsutakse välja alamprogramm kompliment, mis omakorda väljastab ekraanile kaks tervitust (üks nendest seotud alguses sisestatud nimega).
  • Peale AP lausete täitmist jätkub täitmine peaprogrammis, kus küsitakse perekonnanime.
  • Seejärel kutsutakse taas välja alamprogramm kompliment, seekord on aga tegelikuks parameetriks perekonnanimi.
  • Peale alamprogrammis olevate lausete trükkimist läheb tööde järjekord peaprogrammi tagasi, kus töötab viimane print-lause.

Muutujate skoop[edit]

Muutuja skoop (Variable Scope) on osa programmist, kus muutuja deklaratsioon kehtib ehk kus muutuja on nähtav. Skoobi probleemid on olulised ja nendega tuleb osata arvestada. Nagu üldiselt, nii ka Pythonis saab muutujal olla lokaalne või globaalne skoop.

Lokaalsed muutujad
kuuluvad reeglina mõne funktsiooni juurde ja on lokaalsed selle funktsiooni suhtes. Nad tekivad funktsiooni töö käigus ja kaovad siis, kui funktsioon töö lõpetab.
Globaalsed muutujad
"elavad" seni kuni kogu programm töötab ja neid on võimalik kätte saada suvalisest funktsioonist, mis selle programmi käigus käivitatakse.

Identifikaatorit (muutuja nime) otsides käitub Python tavapärasel viisil: kõigepealt otsitakse nime lokaalsete nimede hulgast, kui sealt ei leitud, siis globaalsete hulgast ja kui sealt ka ei leitud, siis tekib viga NameError.

Mis juhtub kui lokaalne ja globaalne muutuja kannavad sama nime? Lähtudes eelnevalt kirjeldatud loogikast leiab interpretaator kõigepealt üles lokaalse muutuja ja tema väärtusega ka tegelema hakatakse.

Peaprogrammi (kõrgeam ploki) muutujad ei ole Pythonis automaatselt globaalsed muutujad. Globaalsete muutujate kasutamiseks tuleb nad kirjeldada võtmesõna global kasutades. Vastav lause lisatakse funktsiooni sisse, enne vastava muutuja kasutamist (mõistlik oleks lisada funktsiooni algusesse kõigi kasutatavate globaalsete muutujate kohta, et inimesel oleks mugavam kõiki globaalseid muutujaid üles leida).

global muutuja1 [, muutuja2, ...]

Samas peab kirjeldatav muutuja olema peaprogrammis deklareeritud (kasutusele võetud), muidu tekib viga. Siiski globaalsete muutujate kasutamine ei ole hea komme, eriti mitte nende muutmine. Täpsustuseks siiski niipalju, et global-deklaratsiooni kasutamata on peaprogrammi muutujad nähtavad, st neid saab lugeda ja nende väärtuseid kasutada. Muudetavaks muutuvad nad aga peale deklareerimist. Kokkuvõttes – parem kui seda võimalust ei kasutata.

Moodulid[edit]

Moodulite kasutamine on meetod selleks, et programmi koodi loogiliselt organiseerida. Kui programmeerimiskeel lubab kasutada mooduleid, siis reeglina saab ühe programmi koodi jaotada füüsiliselt mitmesse faili ja sellega muuta kogu kood hallatavamaks. Moodulid võimaldavad ka koodi korduvkasutada (reuse), mis tarkvaratootmises on väga oluline. Ühte moodulisse saab koguda mitmed funktsioonid (mis tavaliselt omavahel loogiliselt seotud on). Moodulid võivad sisaldada ka klasse koos klasside atribuutide ja meetoditega (objekt-orienteeritud programmi puhul). Moodulis olevaid funktsioone jms saab kasutada, kui moodul importida:

import moodul1, moodul2, ...

Pythoni valmis mooduleid oleme varem imporitnud seosed näiteks ruutjuure-funktsiooni kasutusvajadusega.

Moodul on funktsioonide organiseerimine loogilisel tasemel. Füüsilisel tasemel moodustab moodul eraldi faili. Pythoni moodul kannab laiendit .py, nagu tavaline programmgi. Moodul on ülesehitatud nagu tavaline Pythoni skript / programm. Ta sisaldab funktsioone ja võib lisaks sisaldada ka nö peaprogrammi, milles olevad laused käivitatakse üks kord mooduli kasutusele võtmisel. Need laused võivad näiteks algväärtustada mingeid muutujaid.

Moodulit saab "tõlkida" bait-koodi (byte-compiled). Sellise baitkoodis mooduli laiendiks saab .pyc. See muudab mooduli laadimise veidi kiiremaks ja võimaldab mooduleid edastada nii, et nende sisu ei ole programmi tekstina loetav ja muudetav.

Nimeruum[edit]

Nimeruum (namespace) on mõiste, mille abil luuakse ühesed seosed objektide ja nende nimede vahel. Reeglina moodustab moodul ühe nimeruumi. Peale mooduli importimist oma programmi kutsutakse tema funktsioone välja:

mooduli_nimi.funktsiooni_nimi()

Siinkohal sobib taas meenutada tuttavat sqrt()-funktsiooni ja reegleid, kuidas teda kasutasime. Nuimeruumi lisamine määrab üheselt, millist funktsiooni kasutada. Erinevates moodulites võivad funktsioonide nimed korduda.

Lisaks moodulite nimeruumidele räägitakse lokaalsest nimeruumist ja globaalsest nimeruumist. Näiteks konkreetses programmis kasutatavad muutujad satuvad lokaalsesse nimeruumi ja lokaalne nimeruum muutub, sest funktsioonide väljakutsed muudavad seda. Lõpuks tekib küsimus, mis vahe on nimeruumil ja skoobil.

  • Nimeruum – see on üksühese vastavuse loomine objekti ja nime vahel: millises nimeruumis on milline objekt? Ja võimalus kasutada sama nimega objekte erinevates nimeruumides.
  • Skoop – see on objektide / muutujate nähtavus programmi täitmise konkreetsel hetkel: nt millised muuutjad on selles programmi punktis antud hetkel nähtavad ja millisten muutujate väärtuseid antud hetkel kasutada saab.

Näide moodulist: Järgnevas moodulis on mõned väikesed teisendusfunktsioonid. Eraldi failis on programm, mis antud mooduli impordib ning selles olevaid funktsioone kasutab.

# -*- coding: ISO-8859-4 -*-
# Moodul "teisenda"
# Mooduls on funktsioonid erinevate mõõtühikute vaheliste teisenduste jaoks

def inch2cm(yhikud):
    "Funktsioon teisendab sisendiks olevad tollid sentimeetriteks"
    tulemus = yhikud * 2.54
    return tulemus

def cm2inch(yhikud):
    "Funktsioon teisendab sisendiks olevad sentimeetrid tollideks"
    tulemus = yhikud * 1/2.54
    return tulemus

Moodulit kasutab järgmine testimisprogramm:

# Programm, mis proovib kasutada moodulit teisenda.py
import teisenda
arv = float(input("Sisesta suurus sentimeetrites "))
print("Teisenduse tulemus on",teisenda.cm2inch(arv),"tolli.")

arv = float(input("Sisesta suurus tollides "))
print("Teisenduse tulemus on",teisenda.inch2cm(arv),"sentimeetrit.")


< 10. nädala teemad