Függvények bevezetés

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

In [ ]:
def fv_neve(n):
    "... kód ..."

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 [1]:
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 visszatér 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 [2]:
negyzetel([4, 3, 5])
Out[2]:
[16, 9, 25]

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

In [3]:
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 [4]:
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 ez egy lehetséges hibaforrás.

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

A paramétereket szóközzel választjuk el:

In [5]:
def skalaris_szorzat(v1, v2):
    osszeg = 0
    for i in range(len(v1)):
        osszeg += v1[i] * v2[i]
    return osszeg
In [6]:
skalaris_szorzat([2, 3, 5], [1, 5, 2])
Out[6]:
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 [7]:
def ures_lista():
    return []
In [8]:
L = ures_lista()
L.append(5)
print L
[5]

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 [9]:
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 [10]:
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, csak visszatér egy rendezett listával.

Ez a hozzáállás 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. Ezt a megállapodást 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 [11]:
def prim_e(n):
    for oszto in range(2, n/2):
        if n % oszto == 0:
            return False
    return True
In [12]:
print prim_e(15)
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.

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. Egy kódolási stílus szerint (nem kell követni, főleg nem így az elején) 4-5 sornál hosszabb függvényeket tilos írni. Így a függvényeink rövidek és egyértelműek lesznek. A működésüket nem kommentekkel, hanem a változó és függvénynevekkel írjuk le.

Nézzünk egy példát:

Írjunk egy függvényt, melynek bemenete egy lista, a függvény keresse meg a lista legkisebb és legnagyobb elemét és nullázza ki az összes valamelyikükkel azonos értéket a listában!

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
  • Adott elemmel egyenlő elemek kinullázása egy listában

Oldjuk is meg ezeket:

In [13]:
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ítek a listáról (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 [14]:
def min_max_nullaz(L):
    minelem = minimum(L)
    maxelem = maximum(L)
    ujL = kinullaz(L, minelem)
    ujL = kinullaz(ujL, maxelem)
    return ujL
In [15]:
min_max_nullaz([2, 3, 1, 4, 6, 2, 9, 3, 1, 3, 1, 9, 3, 9])
Out[15]:
[2, 3, 0, 4, 6, 2, 0, 3, 0, 3, 0, 0, 3, 0]
In [16]:
min_max_nullaz([])
Out[16]:
[]
In [17]:
min_max_nullaz([1, 1, 1, 2])
Out[17]:
[0, 0, 0, 0]

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

In [ ]:
    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 [18]:
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ítek a listáról (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 [19]:
min_max_nullaz2([2, 3, 1, 4, 6, 2, 9, 3, 1, 3, 1, 9, 3, 9])
Out[19]:
[2, 3, 0, 4, 6, 2, 0, 3, 0, 3, 0, 0, 3, 0]

Olvashatóság szempontjából az első megoldás sokkal szebb, bár a második is jó megoldás.

Ciklusok ciklusokban, bonyolultabb algoritmusok

Rendezés

Í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 [20]:
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]
    return ujL
In [21]:
buborek([2, 3, 1, 4, 6, 2, 9, 3, 1, 3, 1, 9, 3, 9])
Out[21]:
[1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 6, 9, 9, 9]
In [22]:
buborek(range(10, 0, -1))
Out[22]:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

A kód 6-dik sorában kicseréljük a j-edik és (j+1)-edik elemeket a listában, ami a Pythonban egyszerűen megoldható:

In [ ]:
                ujL[j], ujL[j + 1] = ujL[j + 1], ujL[j]

Ezt egyszerű értékadással csak egy segédváltozó bevezetésével végezhetjük el:

In [ ]:
                temp = ujL[j]
                ujL[j] = ujL[j + 1]
                ujL[j + 1] = temp

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

In [23]:
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]:
                temp = ujL[j]
                ujL[j] = ujL[j + 1]
                ujL[j + 1] = temp
    return ujL
In [24]:
buborek_kiir(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[24]:
[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ó.

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 [25]:
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 [26]:
min_rendez([2, 3, 1, 4, 6, 2, 9, 3, 1, 3, 1, 9, 3, 9])
Out[26]:
[1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 6, 9, 9, 9]
In [27]:
min_rendez(range(10, 0, -1))
Out[27]:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [28]:
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 [29]:
min_rendez_kiir(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[29]:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [30]:
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[30]:
[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.

Tömbök

Pythonban a 2-dimenziós tömböket listák listájával, a 3-dimenziósakat listák listájának listájával... tudjuk legegyszerűbben reprezentálni:

In [31]:
M = [[1, 2], [3, 4]]

Tömb egy elemét így érhetjük el:

In [32]:
M[0][1]
Out[32]:
2
In [33]:
M = [[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[0, 9], [0, 0]]]  # 3x2x2-es tömb
In [34]:
M[2][0][1]
Out[34]:
9

Írjunk olyan függvényt, mely kiír egy 2D tömböt táblázatszerűen ilyesmi formában:

1 2
3 4

In [35]:
def tomb_kiir(M):
    for i in range(len(M)):
        for j in range(len(M[i])):
            print M[i][j], "\t",   # TAB karakter
        print
In [36]:
tomb_kiir([[1,2,3],[4,5,6],[7,8,9]])
1 	2 	3 	
4 	5 	6 	
7 	8 	9 	
In [37]:
tomb_kiir([[1,2,3, 3.5],[4,5,6, 6.5],[7,8,9, 9.5]])
1 	2 	3 	3.5 	
4 	5 	6 	6.5 	
7 	8 	9 	9.5 	

Bonyolultabb adatszerkezetek

Törzsvásárlói kedvezményt szeretnénk adni a vásárlóinknak. Adott a nevük (egyedi, string) és az eddigi vásárlásaik végösszege (egyenként). Minden ügyfélhez egy lista tartozik, melynek első eleme a személy neve, a második egy lista a vásárlások összegéről, például:
["Anett", [54, 23, 12, 56, 12, 71]]
.

A kedvezményt a következő módon adnánk:

Összes vásárlás > 200: 10%
Összes vásárlás > 500: 15%
Összes vásárlás > 1000: 20%

Írjuk meg a függvényt, mely megkapja a vásárlók listáját (elemei, mint a fenti "Anett" lista) és visszaad egy listát melyben 2 elemű listák vannak, az első elem a vásárló neve, a második a kedvezménye. Pl:
["Anett", 10]

Hogyan fogjunk neki? Ismét bontsuk részfeladatokra:

  • Vásárló listából kinyerni a vásárlások szummáját
  • A szummából eldönteni a kedvezményt
  • Az egészet elvégezni a teljes vásárló listára
In [38]:
def kedvezmeny(vasarlok):
    kedvezmenyek = []
    for vasarlo in vasarlok:
        kedvezmenyek.append(kedvezmeny_szamol(vasarlo))
    return kedvezmenyek

def kedvezmeny_szamol(vasarlo):
    nev = vasarlo[0]
    osszeg = 0
    for vasarlas in vasarlo[1]:
        osszeg += vasarlas
    return [nev, osszegbol_kedvezmeny(osszeg)]

def osszegbol_kedvezmeny(osszeg):
    if osszeg > 1000:
        return 20
    if osszeg > 500:
        return 15
    if osszeg > 200:
        return 10
    return 0
In [39]:
kedvezmeny([["Anett", [54, 23, 12, 56, 12, 71]], 
            ["Bela", [11, 3, 12, 1, 12, 55]],
            ["Hagrid", [111, 545, 343, 56, 12, 66]], 
            ["Not_a_wizard", [54, 222, 65, 56, 43, 71]]])
Out[39]:
[['Anett', 10], ['Bela', 0], ['Hagrid', 20], ['Not_a_wizard', 15]]
In [ ]: