Spremenljivkam x, y, ..., z rečemo argumenti
funkcije. Rezultat funkcije vrnemo s stavkom return.
V dokumentacijo napišemo, kaj funkcija dela.
Dokumentacije funkcij ni
potrebno pisati, je pa koristno, sploh pri pisanju večjih programov, v katerih
je definirano veliko število funkcij, in kadar obstaja možnost, da bomo
program v prihodnosti še uporabljali ali pa ga bo uporabljal nekdo drug.
Ta se bo v naši kodi veliko bolje znašel, če bo vsaka funkcija opremljena
z opisom in navodili, kako se jo uporablja. Tudi vse vgrajene funkcije imajo
spisano dokumentacijo.
Kadar nismo prepričani, kaj določena funkcija dela, si lahko pomagamo
z vgrajeno funkcijo help, ki vrne dokumentacijo funkcije:
>>> help(ime_funkcije)
"dokumentacija"
Poglejmo primer enostavne funkcije, ki sprejme dve števili x in y ter vrne
njuno razliko:
def razlika(x, y):
"""Funkcija vrne razliko podanih dveh števil."""
return x - y
Če želimo izvedeti nekaj več o zgornji funkciji, uporabimo spodnji ukaz:
>>> help(razlika)
"Funkcija vrne razliko podanih dveh števil."
Pozicijski in poimenovani argumenti - razlaga
Argumentom, ki smo jih podajali funkcijam v vseh zgornjih primerih, rečemo
pozicijski argumenti. Te argumente moramo pri
klicu funkcije podati v
enakem vrstnem redu, kot smo jih podali pri njeni definiciji.
Poleg pozicijskih poznamo tudi poimenovane argumente
, ki jim ob definiciji funkcije nastavimo privzete vrednosti.
Poglejmo primer funkcije, ki sprejme dva pozicijska argumenta a in b ter
dva poimenovana argumenta c in d:
def moja_fun(a, b, c=1, d=4):
"""Vrne urejen par števil - prvo število predstavlja vsoto spremenljivk a in c,
drugo pa vsoto spremenljivk b in d."""
return a + c, b + d
Kadar želimo funkcijo klicati s privzetimi vrednostmi poimenovanih argumentov,
nam jih ob klicu funkcije ni potrebno navesti. V spodnjem primeru ima
spremenljivka c vrednost $1$, spremenljivka d pa vrednost $4$.
>>> moja_fun(2, 5)
(3, 9)
Poimenovanim argumentom lahko ob klicu funkcije spremenimo vrednosti:
>>> moja_fun(2, 5, c=4, d=6)
(6, 11)
pri čemer nam jih ni potrebno podati v pravilnem vrstnem redu:
>>> moja_fun(2, 5, d=6, c=4)
(6, 11)
Vrednosti lahko spremenimo tudi le nekaterim izmed poimenovanih argumentov,
pri čemer bodo tisti, katerih vrednosti nismo spremenili, obdržali privzete
vrednosti.
Spremenljivka c v spodnjem primeru tako ohrani privzeto vrednost $1$.
>>> moja_fun(2, 5, d=6)
(3, 11)
Poimenovane argumente lahko ob klicu funkcije podajamo na enak način kot
pozicijske, vendar se v tem primeru upošteva njihov vrstni red iz definicije
funkcije. V spodnjem primeru bo spremenljivka c dobila vrednost $4$,
spremenljivka d pa vrednost $6$.
>>> moja_fun(2, 5, 4, 6)
(6, 11)
Pozicijske argumente moramo vedno podati pred poimenovanimi! Spodnji klic
bo v Pythonu sprožil napako.
>>> moja_fun(c=2, d=6, 2, 4)
*args in **kwargs
Včasih potrebujemo funkcijo, ki sprejme zelo veliko število argumentov in si
je zato težko zapomniti, kako si argumenti sledijo po vrsti. V takih primerih
lahko definiramo funkcijo, ki sprejme poljubno število argumentov.
Rezervirana beseda za poljubno število pozicijskih
argumentov v Pythonu je *args,
rezervirana beseda za poljubno število poimenovanih
argumentov pa **kwargs, kjer je
zelo pomembno, da pri prvem uporabimo eno zvezdico, pri drugem pa dve
zvezdici.
Poglejmo primer funkcije, ki sprejme poljubno število pozicijskih in
poljubno število poimenovanih argumentov ter jih izpiše na zaslon:
def f(*args, **kwargs):
"""Izpiše vrednosti args in kwargs na zaslon."""
print("args:", args)
print("kwargs:", kwargs)
>>> f(1, 2, 3, x=4, y=5)
"args: (1, 2, 3)"
"kwargs: {'x': 4, 'y': 5}"
Kot vidimo, se pozicijski argumenti shranijo v terko, poimenovani argumenti
pa v slovar. Slovarje in terke bomo obravnavali kasneje, zato se z njimi ne
obremenjujte. K tem primerom se lahko brez slabe vesti vrnete potem, ko
boste predelali vso snov in osvojili vso potrebno znanje za
razumevanje terk in slovarjev.
Ker se v tem poglavju razlaga uporabo funkcij, pa so stvari vseeno
razložene na tem mestu.
Funkcijo, za katero vemo, da bo vedno sprejela dva pozicijska argumenta,
ne vemo pa, ali jima bodo sledili še kaki drugi pozicijski argumenti,
definiramo na naslednji način:
def f(x, y, *args):
"""Izpiše vrednosti x, y in args na zaslon."""
print("pozicijska x in y:", x, y)
print("args:", args)
>>> f(1, 2, 3, 4)
"pozicijska x in y: 1 2"
"args: (3, 4)"
>>> f(1, 2)
"pozicijska x in y: 1 2"
"args: ()"
Zgornjo funkcijo lahko razširimo še za sprejemanje poimenovanih argumentov.
Recimo, da želimo definirati funkcijo, ki bo omogočala vnos pozicijskih
argumentov na enak način kot zgornja funkcija, pri vsakem klicu pa bo imela
dostop tudi do spremenljivk u in v, ki jima
dodelimo privzete vrednosti, pri čemer pa ne vemo, ali bo pri klicu funkcije
potrebno
podati še kake druge poimenovane argument ali ne. Tako funkcijo definiramo
takole:
def f(x, y, *args, u=10, v=20, **kwargs):
"""Izpiše vrednosti x, y, args, u, v in kwargs na zaslon."""
print("pozicijska x in y:", x, y)
print("args:", args)
print("kwargs:", kwargs)
print("poimenovana u in v:", u, v)
>>> f(1, 2, 3, 4, u=6, v=5, z=7)
"pozicijska x in y: 1 2"
"args: (3, 4)"
"poimenovana u in v: 6 5"
"kwargs: {'z': 7}"
>>> f(1, 2, 3, 4, z=7, v=5, u=6)
"pozicijska x in y: 1 2"
"args: (3, 4)"
"poimenovana u in v: 6 5"
"kwargs: {'z': 7}"
>>> f(1, 2, 3, 4, z=7)
"pozicijska x in y: 1 2"
"args: (3, 4)"
"poimenovana u in v: 10 20"
"kwargs: {'z': 7}"
Anonimne funkcije - razlaga
Včasih neko funkcijo potrebujemo le za kratek čas in je zato nima smisla
shranjevati v spremenljivko.
Definirajmo funkcijo dvakrat, ki sprejme
število x in vrne njegov dvakratnik:
def dvakrat(x):
"""Vrne dvakratnik števila x."""
return 2 * x
Zgornja funkcija je shranjena v spremenljivki dvakrat. Če funkcije ne želimo
shraniti v spremenljivko, jo lahko zapišemo v anonimni obliki na naslednji
način:
lambda x: 2 * x
kjer beseda lambda pove, da gre za funkcijo. Črke, ki sledijo tej besedi
in se nahajajo pred : so argumenti funkcije, tisto kar sledi, pa je rezultat
funkcije.
Če želimo to funkcijo klicati recimo na številu $4$, to naredimo tako:
>>> (lambda x: 2 * x)(4)
8
Tudi funkcije, ki ne sprejmejo nobenega argumenta, lahko definiramo na anonimen način.
Anonimno funkcijo, ki vedno vrne vrednost $3$, zapišemo kot:
lambda: 3
in jo kličemo tako:
>>> (lambda: 3)()
3
Če želimo, lahko funkcijo, ki je zapisana v anonimni obliki, shranimo
v spremenljivko:
dvakrat = lambda x: 2 * x
in jo nato pokličemo na enak način kot bi jo sicer:
>>> dvakrat(4)
8
Spodaj je podan primer anonimne funkcije, ki sprejme več kot le en argument:
lambda x, y, z: x + y + z
Njen klic izgleda tako:
>>> (lambda x, y, z: x + y + z)(3, 4, 7)
14
Anonimne funkcije
V razdelku "Anonimne funkcije - razlaga" si lahko preberete, kaj so anonimne
funkcije in kako z njimi delamo v Pythonu.
Tu pa rešite osnovno nalogo iz te teme.
1. podnaloga
Sestavite funkcijo kvadrat(x), ki sprejme število x in vrne njegov
kvadrat. Kvadrat števila je zmnožek števila s samim seboj.
Funkcijo definirajte v anonimni obliki! Zgled:
>>> kvadrat(3)
9
Uradna rešitev
kvadrat = lambda x: x ** 2
Funkcije višjega reda - razlaga
Funkcije lahko kot argumente sprejmejo tudi druge funkcije ali pa jih vračajo kot
rezultat.
Funkcije, ki vračajo funkcije
Spomnimo se funkcije rozle iz sklopa Uvod v funkcije.
def rozle(x):
"""Vrne za 1 povečano vrednost funkcije bedanec z argumentom 10."""
def bedanec(y):
"""Vrne vsoto števil x in y."""
return x + y
return bedanec(10) + 1
>>> rozle(5)
16
Zgornja funkcija, zapisana kot je, ni funkcija višjega reda, saj ne sprejema
in niti ne vrača funkcije. Lahko pa jo malenkost spremenimo tako, da bo
namesto vrednosti vračala funkcijo bedanec:
def rozle(x):
"""Vrne funkcijo bedanec."""
def bedanec(y):
"""Vrne vsoto števil x in y."""
return x + y
return bedanec
Funkcija rozle je sedaj funkcija, ki sprejme število x in vrne funkcijo bedanec.
Sedaj ob klicu:
>>> rozle(5)
<function rozle.<locals>.bedanec at 0x000001DC6C681D90>
ne dobimo vsote števil x in y, saj funkcija bedanec, ki jo fukcija
rozle vrača,
še ni poklicana. Klic rozle(5) nam torej vrne funkcijo, ki jo je potrebno
še poklicati.
To naredimo tako:
funkcija = rozle(5)
>>> funkcija(10)
15
oziroma kar:
>>> rozle(5)(10)
15
Funkcijo bedanec bi lahko definirali tudi kot anonimno funkcijo:
def rozle(x):
"""Vrne funkcijo, ki sprejme število y in vrne vsoto števil x in y."""
return lambda y: x + y
>>> rozle(5)(10)
15
Lahko gremo še korak dlje in tudi funkcijo rozle zapišemo kot anonimno:
>>> rozle = lambda x: lambda y: x + y
>>> rozle(6)(12)
18
oziroma kar:
>>> (lambda x: lambda y: x + y)(6)(12)
18
Funkcije, ki sprejemajo funkcije
Recimo, da želimo definirati funkcijo trikrat, ki vne trikratnik nekega števila
pri tem pa si želimo pomagati z že definirano funkcijo dvakrat.
def dvakrat(x):
"""Vrne dvakratnik števila x."""
return 2 * x
Funkcijo trikrat lahko sedaj definiramo tako, da kot prvi argument sprejme
funkcijo dvakrat, kot drugi argument pa število x. Funkcija nato
trikratnik števila x izračuna tako, da najprej izračuna dvakratnik tega števila,
nato pa mu prišteje število x.
def trikrat(funkcija, x):
"""Vrne trikratnik števila x."""
return funkcija(x) + x
>>> trikrat(dvakrat, 4)
12
Funkcija trikrat torej kot prvi argument sprejme že definirano funkcijo
dvakrat.
Pokazali smo že, kako se to funkcijo zapiše v anonimni obliki.
Pri klicu funkcije trikrat lahko ta zapis direktno uporabimo in funkcije
dvakrat tako
ne rabimo definirati predhodno:
>>> trikrat(lambda x: 2 * x, 4)
12
Funkcije višjega reda
V razdelku "Funkcije višjega reda - razlaga" si lahko preberete,
kaj so funkcije višjega reda in kako delamo z njimi v Pythonu.
Tu pa rešite osnovno nalogo iz te teme.
1. podnaloga
Sestavite funkcijo višjega reda misi(a), ki kot argument
dobi število miši $a$ in vrne funkcijo $t \mapsto a * 2^t$.
S to funkcijo izračunamo, približno koliko miši bomo imeli čez
t tednov, pri čemer upoštevamo, da se število miši vsak teden podvoji.
Opomba: V resnici nismo upoštevali, da miš spolno dozori približno v
starosti devetih tednov. Brejost traja približno 20 dni, v leglu pa je
od tri do osem mladičev.
Zgled
>>> f = misi(2)
>>> f(5)
64
>>> f(0.5)
2.8284271247461903
Uradna rešitev
def misi(a):
"""Vrne funkcijo, ki sprejme število tednov t ter vrne a * 2**t."""
return lambda t: a * 2**t