Funktiot ovat koodausta helpottava juttu, joiden avulla pystyy selkeyttämään koodia, vähentämään toistoa ja jakamaan koodia osiin. Niitä kannattaa opetella tekemään, koska nätisti osiin jaetun koodin kanssa on paljon helpompaa ja mukavempaa työskennellä :)

Funktiot matematiikassa

Muistellaan, mitä funktio tarkoittaa matematiikassa. Funktio ottaa jonkun luvun (tai monta lukua), ja laskee niiden perusteella jonkun tuloksen:

Esimerkiksi tämä funktio nimeltä f laskee mitä on luku kerrottuna kahdella. Funktio määritellään matematiikassa näin:

f(x) = 2x

Funktiolle annettavaa lukua kuvaava x-muuttuja nimetään funktion nimen vieressä olevien sulkujen sisällä, ja sitten sitä käytetään funktion laskulausekkeessa kohdissa, johon halutaan sijoittaa funktiolle annettu arvo:

f(3) = 2*3 = 6

x-muuttujaa voidaan käyttää useassa eri funktiossa argumentin nimenä, ja tämä ei haittaa eikä vaikuta mitenkään muihin funktioihin.

Funktiot Pythonissa

Myös Pythonissa pystyy tekemään funktioita. Niillä pystyy tekemään paljon muitakin juttuja kuin numeroiden käsitteleminen, mutta jos funktioiden käsittely sujuu matematiikassa, niin ohjelmointifunktioiden vertaaminen matikkafunktioihin saattaa auttaa ymmärtämään niitä paremmin.

Määritellään yllä oleva funktio f Pythonissa:

def f(x):
    return 2*x

Käydään koodi läpi rivi riviltä. Ensimmäinen sana, def, merkitsee sitä, että ollaan määrittelemässä uutta funktiota (def = define = määrittele). Sitten keksitään funktiolle nimi, tässä tapauksessa f (mutta voi olla mikä tahansa, tässä parempi nimi voisi olla tuplaa). Sitten sulkujen sisällä määritellään, mitä parametreja funktio ottaa. Parametri siis tarkoittaa funktion ottamaa arvoa jota se käsittelee jotenkin. Tässä esimerkissä funktio ottaa vain yhden parametrin, joka on numero jonka se tuplaa. Tälle parametrillekin voi antaa minkä tahansa nimen.

Kaksoispiste tarkoittaa, että seuraavalta riviltä alkaa funktion oma lohko.

Mistä se parametri oikeen tulee???

Parametri luodaan/määritellään juuri tuolla ekalla rivillä. Eli kun kirjoitat sulkujen sisään parametreja, niin siinä määrittelet niiden nimet (ja niiden olemassaolon).

Jos olet kirjoittanut def-riville tietyt parametrit, niin sellaisia parametreja funktio odottaa saavansa, ja jos yrität antaa funktiolle enemmän tai vähemmän argumentteja kuin def-rivillä on määritelty, niin siitä tulee errori.

Funktiolohkon sisällä oleva koodi määrittelee, mitä funktio oikein tekee. Tässä funktiossa sattuu olemaan vain yksi rivi, return 2*x. Return (suomeksi palauta) tarkoittaa sitä, että funktio loppuu, ja jokin arvo palautetaan funktion tuloksena (tässä tapauksessa 2*x).

Nyt kun funktio on määritelty, niin sitä voi käyttää esimerkiksi näillä tavoilla:

print(f(100))

tuplattava = 5
print(tuplattava, "tuplattuna:", f(tuplattava))

for i in range(f(2)):
    print(i)
200
5 tuplattuna: 10
0
1
2
3

Funktion käyttämistä näin sanotaan kutsumiseksi. Eli tämä on funktiokutsu: f(2). Vaikka funktio ei ottaisi yhtään parametria, niin sen kutsuminen vaatii silti sulut: print(). Jos kirjoitat koodiin pelkästään funktion nimen ilman sulkuja, niin se ei tee mitään.

Jos osaat jo käyttää muuttujia koodin seassa, niin funktiokutsu toimii vähän samalla tavalla. Vertaa näitä:

# aika turha funktio joka vaan palauttaa arvon 5
def viis():
    return 5

# muuttuja jonka arvo on 5
viitonen = 5

print(viis())
print(viitonen)
print(viis() + 2)
print(viitonen + 2)
5
5
7
7
Argumentti? Parametri?

Sanoja parametri ja argumentti käytetään usein sekaisin, mutta jos tarkkoja ollaan, niin parametri tarkoittaa def-rivillä funktion sulkujen sisällä määriteltyä muuttujaa, kun taas argumentti tarkoittaa funktiolle käsiteltäväksi annettavaa oikeaa arvoa.

# tuossa luku on parametri
def tuplaa(luku):
    return luku * 2

# tuossa 5 on argumentti
print(tuplaa(5))

Funktio voi käsitellä mitä tahansa datatyyppejä. Tässä esimerkki tuplen käsittelystä. Jos et vielä tiedä mikä on tuple, niin se on vähän niin kuin lista, mutta sitä ei pysty muokkaamaan luomisen jälkeen (eli sen yksittäisiä alkioita ei pysty muuttamaan tai poistamaan). Tuple on kätevä tapa laittaa monta tiedonpalasta yhdeksi paketiksi.

# tässä oletetaan, että tuote-parametriksi annetaan tuple,
# joka sisältää nämä alkiot:
# 0. tuotteen nimi (merkkijono)
# 1. tuotteen määrä varastossa (kokonaisluku)
# 2. tuotteen kuvaus (merkkijono)
def muunna_merkkijonoksi(tuote):
    # otetaan selkeyden vuoksi tuplen alkiot muuttujiin
    nimi = tuote[0]
    maara = str(tuote[1])
    kuvaus = tuote[2]
    
    # muodostetaan alkioista yksi merkkijono
    tulos = ""
    # \n tarkoittaa rivinvaihtomerkkiä
    tulos += nimi + "\n"
    tulos += "Varastossa: " + maara + " kpl\n"
    tulos += kuvaus

    return tulos

Tässä funktiossa on paljon enemmän rivejä kuin tuplaamisfunktiossa. Ajattele, jos tästä ei tekisi funktiota, vaan tuplen käsittelyn kirjoittaisi suoraan koodin sekaan. Jos merkkijonoksi muuntamista tarvittaisiin koodissa monessa kohtaa, niin koodiin tulisi paljon extrarivejä, joissa sanottaisiin täysin sama asia. Lisäksi koodista tulisi sekavampi. Tämän takia koodia kannattaa jakaa funktioihin, etenkin jos samaa toiminnallisuutta tarvitaan useammin kuin kerran.

Tässä vielä esimerkki tämän funktion käytöstä, jos se jäi epäselväksi.
tuotteet = [
    ("Paita", 55, "Tosi hieno paita"),
    ("Kirja", 38, "Luontokuvia sisältävä kirja"),
    ("Luistimet", 11, "Valkoiset taitoluistimet")
]

for tuote in tuotteet:
    print(muunna_merkkijonoksi(tuote))
    print("-----")
Paita
Varastossa: 55 kpl
Tosi hieno paita
-----
Kirja
Varastossa: 38 kpl
Luontokuvia sisältäviä kirja
-----
Luistimet
Varastossa: 11 kpl
Valkoiset taitoluistimet
-----

Funktion ei tarvitse palauttaa mitään

Joskus tarvitaan funktiota, joka vain suorittaa tietyt koodirivit, mutta sillä ei ole mitään tulosta, jonka voisi palauttaa. Esimerkki tällaisesta funktiosta on print. Printin tehtävä on pelkästään tulostaa näkyviin sille annetut argumentit.

Testataan mitä tapahtuu, jos printin tuloksen yrittää tulostaa:

# tulostetaan print-kutsun tulos (paluuarvo)
print(print("Moikka!"))
Moikka!
None

Print-funktion tulos on siis None (none = ei mitään). Jos et laita kirjoittamasi funktion sisään lainkaan returnia, tai kirjoitat pelkästään return ilman mitään arvoa, niin funktiosi toimii samalla tavalla kuin print yllä. Siitä tulee siis None.

Sisäänrakennetut funktiot

Pythonissa on monia sisäänrakennettuja funktioita. Tässä muutama esimerkki näistä funktioista.

len

Funktio len ottaa argumentiksi merkkijonon tai listan, ja palauttaa sen pituuden.

nimi = input("Kirjoita nimesi: ")
print(f"Nimesi on {len(nimi)} merkkiä pitkä.")

print

Funktio print ottaa argumentiksi mitä tahansa dataa, ja tulostaa sen. Printille voi antaa montakin argumenttia, jolloin se tulostaa ne välilyönnillä erotettuna.

print("Moikka")
print(["a", "b", "c"])
print("a", "b", "c", 1, 2, 3)
Moikka
['a', 'b', 'c']
a b c 1 2 3

min ja max

min voi ottaa argumentiksi kaksi tai enemmän numeroita, tai listan numeroita. Se palauttaa luvuista pienimmän. max on samanlainen funktio joka palauttaa suurimman luvun.

num1 = int(input("Anna numero: "))
num2 = int(input("Anna toinen numero: "))
suurempi = max(num1, num2)
print(f"Luvuista suurempi on {suurempi}.")

Sisäkkäiset funktiokutsut

Funktioita pystyy kutsumaan sisäkkäin niin monta kuin haluaa. Siis kutsutaan yhtä funktiota, jonka palautusarvo annetaan argumentiksi toiselle funktiolle, ja sitten toisen funktion palautusarvo annetaan argumentiksi kolmannelle funktiolle ja niin edelleen. Edellisen esimerkin voi kirjoittaa vaikka kokonaan yhdelle riville sisäkkäisten funktiokutsujen avulla:

print(f"Luvuista suurempi on {max(int(input('Anna numero: ')), int(input('Anna toinen numero: ')))}.")

Kuitenkin liian monta funktiokutsua sisäkkäin näyttää todella sekavalta, joten niitä ei kannata käyttää liikaa (tässä esimerkissä on f-stringikin sekoittamassa).

Tehtävät

Tehtävä 1
+

Kirjoita funktio, joka ottaa argumentiksi jotain tekstiä ja tulostaa sen, ja lisäksi sen ylä- ja alapuolelle jonkin tekstistä tehdyn kuvion/reunuksen. Tulosta funktiolla jotain tekstiä.

Ratkaisu
def hieno_printtaus(teksti):
    print("-----~.O.~-----")
    print(" ", teksti)
    print("------.v.------")

hieno_printtaus("kehys :)")
Tehtävä 2
+

Kirjoita tämä matemaattinen funktio Pythonina:

f(x) = 3x + 5

Tulosta arvo f(10) viime tehtävässä tehdyllä printtausfunktiolla.

Ratkaisu
def f(x):
    return 3*x + 5

hieno_printtaus(f(10))
Tehtävä 3
+

Kirjoita funktio onko_parillinen, joka ottaa argumentiksi numeron ja palauttaa totuusarvon, joka kertoo, onko luku parillinen. Vinkki: käytä jakojäännös- eli modulo-operaattoria (%).

Kysy käyttäjältä lukua, ja kutsu funktiota. Jos luku on parillinen, niin tulosta jokin tietty viesti. Tulosta jokin toinen viesti jos luku on pariton.

Ratkaisu
def onko_parillinen(luku):
    if luku % 2 == 0:
        return True
    else:
        return False

num = int(input("Anna luku: "))

if onko_parillinen(num):
    print("Luku on parillinen.")
else:
    print("Luku on pariton.")

Lyhyempi tapa kirjoittaa funktio:

def onko_parillinen(luku):
    return luku % 2 == 0