Függvények bevezetés

Python-ban a kódban bárhol definiálható függvény, a def kulcsszóval.

In [1]:
def fv_neve(n):
    pass
In [2]:
fv_neve(5)

Példa függvényre és meghívására

Ez egy függvény definíció, itt csak azt írjuk le, hogy mit csináljon a negyzetel függvény, ha meghívjuk:

In [3]:
def negyzetel(l):
    uj_l = []
    for i in l:
        uj_l.append(i*i)
    return uj_l

A def kulcsszó után a függvény nevét írjuk, utána zárójelekben felsoroljuk a bemeneti paramétereket. A kettőspont után új blokk kezdődik, mint például egy for ciklusban, ez a blokk fut le amikor meghívjuk a függényt.

A blokkon belül új kulcsszó a return mely hatására a függvény futása leáll és az eredménye a return után írt kifejezéssel.

Egy függvényt a nevével tudunk meghívni, mely után zárójelekben a bemeneti argumentumokat kell felsorolni (itt csak egy lista a bemeneti argumentum):

In [4]:
negyzetel([4, 3, 5])
Out[4]:
[16, 9, 25]

Természtesen egy változón is meg lehet hívni a függvényt:

In [5]:
szamok = [5, 1, 8]
negyzetelt_szamok = negyzetel(szamok)
print(szamok, negyzetelt_szamok)
[5, 1, 8] [25, 1, 64]

Mivel "jól" volt megírva a függvényünk így semmi nem kívánt dolog nem történt, de nézzük meg mi lesz, ha ezt a megvalósítást használjuk:

In [6]:
def negyzetel2(l):
    i = 0
    while i < len(l):
        l[i] = l[i] ** 2
        i += 1
    return l


szamok = [5, 1, 8]
negyzetelt_szamok = negyzetel2(szamok)
print(szamok, negyzetelt_szamok)
[25, 1, 64] [25, 1, 64]

Tehát amilyen műveletet a függvényen belül végrehajtottunk a listán az a beadott argumentumon végrehajtódott, megváltozott a szamok lista. Erről egy későbbi előadáson még részletesen beszélünk, addig is tudjunk róla, hogy ilyen is lehetséges.

Példa többváltozós függvényre

A paramétereket vesszővel választjuk el:

In [7]:
def skalaris_szorzat(v1, v2):
    osszeg = 0
    for i in range(len(v1)):
        osszeg += v1[i] * v2[i]
    return osszeg
In [8]:
skalaris_szorzat([2, 3, 5], [1, 5, 2])
Out[8]:
27

A paraméterek bármilyen típusúak lehetnek. Akárhány paramétere lehet egy függvénynek, akár nulla is, a zárójelek ebben az esetben is kellenek:

In [9]:
def üzenet():
    return "Jó reggelt!"
In [10]:
üzenet()
Out[10]:
'Jó reggelt!'

Különbség függvény és metódus között

Első (felszínes) megközelítésben függvényről beszélünk, ha meghívásának módja: függvénynév, majd zárójelpárban az argumentumok felsorolása, metódusról, ha meghívásának módja: az objektum után pont, majd a metódus neve, majd zárójelben az argumentumok. Pl.:

In [11]:
l = [5, 2, 4]
l.sort()
print(l)
[2, 4, 5]

A sort a pythonban egy beépített metódusa a listáknak, mely rendezi az adott listát. Jó példa, mert létezik függvény formában is:

In [12]:
l = [5, 2, 4]
ujl = sorted(l)
print(l, ujl)
[5, 2, 4] [2, 4, 5]

A sorted függvény nem rendezi az argumentumként adott listát, hanem visszaad egy rendezett listát.

Ez a legtöbbször igaz a beépített metódusokra és függvényekre, tehát a függvények nem módosítják a kapott változókat, míg a metódusok módosítják az objektumot (változót) amin meg vannak hívva (ami a pont bal oldalán áll). Ezt a konvenciót hasznos követni és csak olyan függvényeket írni, melyek nem módosítják a kapott változókat.

Metódus írásról később tanulunk.

A return használata

A return utasítás függvényből kilépését ki lehet használni, például:

In [13]:
def prim_e(n):
    oszto = 2
    while oszto ** 2 <= n:
        if n % oszto == 0:
            return False
        oszto += 1
    return True
In [14]:
print(prim_e(4))
print(prim_e(23))
False
True

Itt azt használtuk ki, hogy amint egy return parancshoz ér a függvény azonnal kilép és visszaadja ezt az eredményt. Tehát amint találunk egy osztót azonnal visszatérünk a hamis eredménnyel. Igazzal pedig csak akkor térünk vissza, ha végigért a ciklus, azaz nem találtunk megfelelő osztót.

Megjegyzés: a break egy ciklusból lép ki (csak ciklusból és csak egyből), míg a return függvényből (csak egy függvényből, de annak bárhány ciklusából).

None

return után állhat None, jelezvén, hogy "semmit" adott vissza a függvény.

Ha egy függvény nem találkozik futása során return paranccsal, akkor None-t ad ki eredményül. Például ha elfelejtünk return-t írni vagy rossz helyre tesszük.

Megjegyzés: a .sort() metódus eredénye None, de mellékhatása, hogy közben rendezi a paraméterként kapott listát.

In [15]:
l = [3, 2, 1, 4]
l2 = l.sort()
print(l2, l)
None [1, 2, 3, 4]

Függvények használata függvényekben

Általunk írt függvényeket természetesen használhatunk más általunk írt függvényekben. Ez erősen ajánlott is.

Érdemes 4-5 sornál hosszabb függvényeket mellőzni, pontosabban azokat kisebb függvényekből összerakni. Így a függvényeink rövidek és egyértelműek lesznek. A működésük derüljön ki a változók és magának a függvénynek a nevéből.

Fealadat: Írjunk egy függvényt, melynek bemenete egy lista, melyben megkeresi a legkisebb és legnagyonbb elemet, kimenete pedig egy másik lista, melyben a legkisebb és legnagyobb elemek mind ki vannak nullázva!

Hogyan futunk neki egy ilyen feladatnak?

  1. Részfeladatokra bontjuk
  2. A részfeladatokat megvalósítjuk
  3. Összekötjük a teljes programot

Itt a következő részfeladatokra bontható:

  • Listában minimum keresés
  • Listában maximum keresés
  • A szélsőértékekkel egyenlő elemek kinullázása egy új listában
In [16]:
def minimum(l):
    min_elem = float("inf")
    for e in l:
        if e < min_elem:
            min_elem = e
    return min_elem

def maximum(l):
    max_elem = -float("inf")
    for e in l:
        if e > max_elem:
            max_elem = e
    return max_elem

def kinullaz(l, elem):
    ujl = l[:]     # Másolatot készítése (elejétől végéig részlista)
    for i in range(len(ujl)):
        if ujl[i] == elem:
            ujl[i] = 0
    return ujl

Most, hogy a részfeladatokat már megoldottuk nem maradt hátra más, mint hogy összerakjuk egybe a fő függvényt:

In [17]:
def min_max_nullaz(l):
    minelem = minimum(l)
    maxelem = maximum(l)
    ujl = kinullaz(l, minelem)
    ujl = kinullaz(ujl, maxelem)
    return ujl
In [18]:
min_max_nullaz([2, 3, 1, 4, 6, 2, 9, 3, 1, 3, 1, 9, 3, 9])
Out[18]:
[2, 3, 0, 4, 6, 2, 0, 3, 0, 3, 0, 0, 3, 0]
In [19]:
min_max_nullaz([])
Out[19]:
[]
In [20]:
min_max_nullaz([1, 1, 1, 2])
Out[20]:
[0, 0, 0, 0]

Az utolsó három sor helyett írhattuk volna akár ezt is:

return kinullaz(kinullaz(l, minelem), maxelem)

Természetesen meg lehetett volna oldani ezt a feladatot egy függvénnyel is, itt az eredmény:

In [21]:
def min_max_nullaz2(l):
    min_elem = float("inf")
    for e in l:
        if e < min_elem:
            min_elem = e
            
    max_elem = -float("inf")
    for e in l:
        if e > max_elem:
            max_elem = e
            
    ujl = l[:]     # Másolatot készítése (elejétől végéig részlista)
    for i in range(len(ujl)):
        if ujl[i] == min_elem:
            ujl[i] = 0
            
    for i in range(len(ujl)):
        if ujl[i] == max_elem:
            ujl[i] = 0
            
    return ujl
In [22]:
min_max_nullaz2([2, 3, 1, 4, 6, 2, 9, 3, 1, 3, 1, 9, 3, 9])
Out[22]:
[2, 3, 0, 4, 6, 2, 0, 3, 0, 3, 0, 0, 3, 0]

Az első megoldás:

  • olvashatóbb,
  • könnyebb módosítani rajta valamit,
  • hibákat könnyebb benne megtalálni.
  • És még az is jó benne, hogy utólag fel lehet használni másra a már megírt függvényeket.

Bár a második megoldás is működik.

Ciklusok ciklusokban, bonyolultabb algoritmusok

Rendezés

Feladat: Írjunk függvényt, mely rendez egy adott listát, de nem használja se a sort metódust, se a sorted függvényt!

Első ötlet a buborékrendezés zenés, táncos szemléltetéssel:

In [23]:
def buborek(l):
    ujl = l[:]
    for i in range(len(ujl) - 1):
        for j in range(len(ujl) - i - 1):
            if ujl[j] > ujl[j + 1]:
                ujl[j], ujl[j + 1] = ujl[j+1], ujl[j]
                # temp = ujl[j], ujl[j] = ujl[j + 1], ujl[j + 1] = temp
    return ujl
In [24]:
buborek([2, 3, 1, 4, 6, 2, 9, 3, 1, 3, 1, 9, 3, 9])
Out[24]:
[1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 6, 9, 9, 9]
In [25]:
buborek(list(range(10, 0, -1)))
Out[25]:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Írjuk ki minden lépés után a rendezés aktuális állapotát:

In [26]:
def buborek_kiir(l):
    ujl = l[:]
    for i in range(len(ujl) - 1):
        for j in range(len(ujl) - i - 1):
            print(ujl)                      # Kiírás 
            if ujl[j] > ujl[j + 1]:
                ujl[j], ujl[j + 1] = ujl[j+1], ujl[j]
    return ujl
In [27]:
buborek_kiir(list(range(10, 0, -1)))
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[9, 10, 8, 7, 6, 5, 4, 3, 2, 1]
[9, 8, 10, 7, 6, 5, 4, 3, 2, 1]
[9, 8, 7, 10, 6, 5, 4, 3, 2, 1]
[9, 8, 7, 6, 10, 5, 4, 3, 2, 1]
[9, 8, 7, 6, 5, 10, 4, 3, 2, 1]
[9, 8, 7, 6, 5, 4, 10, 3, 2, 1]
[9, 8, 7, 6, 5, 4, 3, 10, 2, 1]
[9, 8, 7, 6, 5, 4, 3, 2, 10, 1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 10]
[8, 9, 7, 6, 5, 4, 3, 2, 1, 10]
[8, 7, 9, 6, 5, 4, 3, 2, 1, 10]
[8, 7, 6, 9, 5, 4, 3, 2, 1, 10]
[8, 7, 6, 5, 9, 4, 3, 2, 1, 10]
[8, 7, 6, 5, 4, 9, 3, 2, 1, 10]
[8, 7, 6, 5, 4, 3, 9, 2, 1, 10]
[8, 7, 6, 5, 4, 3, 2, 9, 1, 10]
[8, 7, 6, 5, 4, 3, 2, 1, 9, 10]
[7, 8, 6, 5, 4, 3, 2, 1, 9, 10]
[7, 6, 8, 5, 4, 3, 2, 1, 9, 10]
[7, 6, 5, 8, 4, 3, 2, 1, 9, 10]
[7, 6, 5, 4, 8, 3, 2, 1, 9, 10]
[7, 6, 5, 4, 3, 8, 2, 1, 9, 10]
[7, 6, 5, 4, 3, 2, 8, 1, 9, 10]
[7, 6, 5, 4, 3, 2, 1, 8, 9, 10]
[6, 7, 5, 4, 3, 2, 1, 8, 9, 10]
[6, 5, 7, 4, 3, 2, 1, 8, 9, 10]
[6, 5, 4, 7, 3, 2, 1, 8, 9, 10]
[6, 5, 4, 3, 7, 2, 1, 8, 9, 10]
[6, 5, 4, 3, 2, 7, 1, 8, 9, 10]
[6, 5, 4, 3, 2, 1, 7, 8, 9, 10]
[5, 6, 4, 3, 2, 1, 7, 8, 9, 10]
[5, 4, 6, 3, 2, 1, 7, 8, 9, 10]
[5, 4, 3, 6, 2, 1, 7, 8, 9, 10]
[5, 4, 3, 2, 6, 1, 7, 8, 9, 10]
[5, 4, 3, 2, 1, 6, 7, 8, 9, 10]
[4, 5, 3, 2, 1, 6, 7, 8, 9, 10]
[4, 3, 5, 2, 1, 6, 7, 8, 9, 10]
[4, 3, 2, 5, 1, 6, 7, 8, 9, 10]
[4, 3, 2, 1, 5, 6, 7, 8, 9, 10]
[3, 4, 2, 1, 5, 6, 7, 8, 9, 10]
[3, 2, 4, 1, 5, 6, 7, 8, 9, 10]
[3, 2, 1, 4, 5, 6, 7, 8, 9, 10]
[2, 3, 1, 4, 5, 6, 7, 8, 9, 10]
[2, 1, 3, 4, 5, 6, 7, 8, 9, 10]
Out[27]:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Ennél hatékonyabb algoritmusok léteznek a keresésre, ezekről részletesebben az Algoritmuselmélet című tárgyban lesz szó.

Feladat: Valósítsuk meg még azt az ötletet, hogy egyesével megkessük a legkisebb, a második legkisebb, stb. elemet és ezeket a megfelelő helyre tesszük.

In [28]:
def minimum_index(l):
    min_index = 0
    for i in range(len(l)):
        if l[i] < l[min_index]:
            min_index = i
    return min_index

def min_rendez(l):
    ujl = l[:]
    for i in range(len(ujl) - 1):
        minindex = i + minimum_index(ujl[i:])
        ujl[i], ujl[minindex] = ujl[minindex], ujl[i]
    return ujl
In [29]:
min_rendez([2, 3, 1, 4, 6, 2, 9, 3, 1, 3, 1, 9, 3, 9])
Out[29]:
[1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 6, 9, 9, 9]
In [30]:
min_rendez(list(range(10, 0, -1)))
Out[30]:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [31]:
def min_rendez_kiir(l):
    ujl = l[:]
    for i in range(len(ujl) - 1):
        print(ujl)
        minindex = i + minimum_index(ujl[i:])
        ujl[i], ujl[minindex] = ujl[minindex], ujl[i]
    return ujl
In [32]:
min_rendez_kiir(list(range(10, 0, -1)))
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[1, 9, 8, 7, 6, 5, 4, 3, 2, 10]
[1, 2, 8, 7, 6, 5, 4, 3, 9, 10]
[1, 2, 3, 7, 6, 5, 4, 8, 9, 10]
[1, 2, 3, 4, 6, 5, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Out[32]:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [33]:
min_rendez_kiir([4, 5, 2, 3, 6, 7, 1])
[4, 5, 2, 3, 6, 7, 1]
[1, 5, 2, 3, 6, 7, 4]
[1, 2, 5, 3, 6, 7, 4]
[1, 2, 3, 5, 6, 7, 4]
[1, 2, 3, 4, 6, 7, 5]
[1, 2, 3, 4, 5, 7, 6]
Out[33]:
[1, 2, 3, 4, 5, 6, 7]

A kiírástól csalóka lehet, mert kevesebb kimenetet kaptunk, ez azért van, mert itt csak a külső for ciklusban van a kiírás, nem a belsőben. Itt a belső ciklus el van rejtve a minimum_index függvényben.