07. MODUL · PYTHON

Saját függvények.

def, paraméter, return, hatókör

1. fejezet

Miért írunk saját függvényt?

Eddig is sok függvényt használtál: print(), input(), len(), random.randint(). Ezeket mások írták meg, te csak meghívod őket – nem kell tudnod, hogyan működnek belül, elég tudnod, mit adsz nekik és mit kapsz vissza. Ugyanilyen egységeket magad is készíthetsz.

Függvény

A függvény egy névvel ellátott, önálló programrész, amely egy jól körülhatárolt feladatot végez el. Bemenetet kaphat (paraméterek), és eredményt adhat vissza (visszatérési érték). Egyszer írjuk meg (definíció), és bárhányszor felhasználhatjuk (hívás).

Nincs ismétlés

Ami többször kell, egyszer írjuk meg. Ha javítani kell, egy helyen javítjuk.

Részekre bontás

A nagy feladat kis, külön-külön átlátható és kipróbálható lépésekre esik szét.

Olvashatóság

Az if prim_e(szam): sor elárulja a szándékot, a ciklus részletei pedig a függvényben maradnak.

2. fejezet

Definíció és hívás

def fuggveny_neve(parameter1, parameter2):
    """Rövid leírás: mit csinál a függvény."""
    utasítások
    return eredmény
  • defkulcsszó: „definiálok egy függvényt”
  • fuggveny_nevea név (snake_case, mint a változóké)
  • (…)a paraméterek listája – lehet üres is
  • : + behúzása függvény törzse
  • returnaz eredmény visszaadása (nem kötelező)
def koszont():
    print("Szia!")
    print("Üdv a függvények világában.")

print("A program eleje.")
koszont()
koszont()
print("A program vége.")

Kövesd Lépésenként, és figyeld a sorrendet. A def sorban a Python csak megjegyzi a függvényt (a Memóriában megjelenik egy ƒ koszont() bejegyzés), de a törzse ekkor nem fut le. A program a „A program eleje.” kiírásával folytatódik; a törzs csak a koszont() hívásakor fut – mindkét hívásnál újra. A hívás végén a program a hívás utáni sorral folytatódik.

A def sornak a hívás végrehajtása előtt le kell futnia, különben NameError a válasz – ezért a fő program hívásai a definíciók alatt állnak. (Egy függvény törzséből hívhatsz később definiált függvényt is, hiszen a törzs csak a hívásakor fut.) A zárójel a hívás része: a koszont zárójel nélkül csak magát a függvényt jelenti, nem futtatja le.

A gyakorló feladatok kezdő kódjában a még üres függvénytörzsben pass áll: ez az üres utasítás, amely ott szerepel, ahol a nyelvtan utasítást követel, de még nincs mit írni.

3. fejezet

Paraméter és argumentum

A paraméter a függvény fejlécében álló név; az argumentum a hívásnál átadott konkrét érték. A hívás pillanatában minden paraméter megkapja a neki megfelelő argumentum értékét – a sorrend dönti el, melyik melyiket, nem a név.

def bemutat(nev, kor):
    print(f"{nev} {kor} éves.")

bemutat("Anna", 17)
bemutat("Bence", 16)
x = "Csilla"
bemutat(x, 15)
bemutat(18, "Dóra")

A lépésenkénti nézetben minden hívásnál új keret (frame) jelenik meg a Memóriában a függvény nevével: ebben vannak a paraméterek a kapott értékkel. Az utolsó hívásnál a sorrend fel van cserélve – a Python ezt nem veszi észre, csak furcsa mondat lesz belőle. A függvény nem tudja, mit „akartunk”, csak azt, amit kapott.

Ha kevesebb vagy több argumentumot adunk át, mint ahány paraméter van, TypeError a válasz: bemutat("Anna") → missing 1 required positional argument: 'kor'. (Az alapértelmezett értékekről és a név szerinti átadásról a 9. modul szól.)

4. fejezet

A return: az eredmény visszaadása

A return két dolgot tesz: kiszámolja a mögötte álló kifejezés értékét, és ezt az értéket visszaadja a hívás helyére, közben azonnal befejezi a függvényt (ami utána áll, már nem fut le). A hívás helyén a függvényhívás „helyére kerül” az eredmény, amit aztán változóba tehetünk, kiírhatunk, vagy egy nagyobb kifejezésben használhatunk.

def negyzet(x):
    return x * x

a = negyzet(5)
print(a)
print(negyzet(3) + negyzet(4))
if negyzet(7) > 40:
    print("49 nagyobb 40-nél.")
negyzet(3) + negyzet(4)→9 + negyzet(4)→9 + 16→25

return vagy print?

A kezdők leggyakoribb tévedése, hogy a függvényben kiírják az eredményt ahelyett, hogy visszaadnák. A print csak megmutatja az értéket a képernyőn – a program többi része nem tud vele tovább számolni. Ha egy függvényben nincs return, az eredménye a None („nincs érték”).

def dupla_kiir(x):
    print(x * 2)

def dupla(x):
    return x * 2

eredmeny1 = dupla(5)
eredmeny2 = dupla_kiir(5)
print("return-nel:", eredmeny1)
print("print-tel:", eredmeny2)
print(eredmeny1 + 1)
print(eredmeny2 + 1)

Az utolsó sor TypeError-ral leáll: a None-hoz nem lehet hozzáadni. Szabály: a függvény számoljon és adjon vissza; a kiírás a hívó dolga. Így a függvény bárhol újra felhasználható – kiírásra, további számolásra, feltételben is.

Mit ír ki?

def f(x):
    if x > 0:
        return "pozitív"
    return "nem pozitív"
    print("Ez sosem fut le.")

print(f(3), f(0))
  1. pozitív pozitív
  2. pozitív nem pozitív
  3. pozitív nem pozitív Ez sosem fut le.

A return azonnal befejezi a függvényt. 3-nál az első return fut le; 0-nál a feltétel hamis, így a második. Az utolsó sor egyik esetben sem érhető el („halott kód”).

Igaz-hamis eredmény

Ha a függvény egy kérdésre válaszol („osztható-e?”, „prím-e?”), logikai értéket adjon vissza. Mivel egy összehasonlítás eredménye már maga is True vagy False, gyakran közvetlenül visszaadható:

def oszthato(szam, oszto):
    return szam % oszto == 0

for ev in [2024, 2025, 1900, 2000]:
    szokoev = (oszthato(ev, 4) and not oszthato(ev, 100)) or oszthato(ev, 400)
    print(ev, "szökőév" if szokoev else "nem szökőév")

A "szökőév" if szokoev else "nem szökőév" egy feltételes kifejezés: ha a feltétel igaz, az első, különben a második érték lesz az eredménye. Például "páros" if 4 % 2 == 0 else "páratlan" értéke "páros".

5. fejezet

Több érték visszaadása

Ha a return után vesszővel több értéket írunk, a függvény egy tuple-t ad vissza, amelyet a hívásnál kicsomagolhatunk (4. modul).

def statisztika(szamok):
    legkisebb = min(szamok)
    legnagyobb = max(szamok)
    atlag = sum(szamok) / len(szamok)
    return legkisebb, legnagyobb, atlag

jegyek = [4, 5, 3, 5, 2]
mini, maxi, atl = statisztika(jegyek)
print(f"Legrosszabb: {mini}, legjobb: {maxi}, átlag: {atl:.2f}")
print(statisztika([10, 20]))

Ha egy függvény modult használ (pl. math.sqrt()), az import sor a fájl elején álljon – különben a függvény hívásakor NameError a válasz.

6. fejezet

Hatókör: helyi és globális változók

A függvényen belül értéket kapó változó (és minden paraméter) helyi (lokális): csak a függvény adott hívásának keretében létezik, és a függvény végén megszűnik. A fő programban létrehozott változó globális: a függvények olvashatják.

afa = 0.27

def brutto(netto):
    ado = netto * afa
    return netto + ado

print(brutto(1000))
print(afa)
print(ado)

Kövesd lépésenként: az ado csak a brutto() keretében jelenik meg, és a visszatéréskor a kerettel együtt eltűnik. Ezért az utolsó sor NameError-t ad – a fő program nem látja a függvény helyi változóját.

Árnyékolás: két azonos nevű változó

Ha a függvényben értéket adunk egy olyan névnek, amely globálisan is létezik, az egy új, helyi változót hoz létre, amely a függvényen belül eltakarja (árnyékolja) a globálisat. A globális változó nem változik meg.

nev = "globális"

def pelda():
    nev = "helyi"
    print("A függvényben:", nev)

pelda()
print("A függvény után:", nev)

A global kulcsszó – és miért kerüljük

Ha a függvénynek tényleg a globális változót kell módosítania, a global nev sorral jelezhetjük. Működik, de a programot nehezen követhetővé teszi, mert egy függvényhívás „észrevétlenül” megváltoztatja a fő program adatait. Jobb megoldás, ha a függvény paraméterként kapja meg, amire szüksége van, és return-nel adja vissza az eredményt.

szamlalo = 0

def novel_global():
    global szamlalo
    szamlalo += 1

def novel(ertek):
    return ertek + 1

novel_global()
novel_global()
print(szamlalo)
szamlalo = novel(szamlalo)
print(szamlalo)

UnboundLocalError. Ha egy függvényben egy globális nevet előbb olvasol, és később értéket is adsz neki, a Python az egész függvényben helyi változónak tekinti – és az olvasáskor még nincs értéke:

pont = 10

def bonusz():
    pont = pont + 5
    return pont

print(bonusz())

Lista mint argumentum

A paraméter ugyanarra az objektumra mutat, amelyre a hívásnál átadott érték (a 4. modul névcédula-modellje szerint). Számnál és szövegnél ez nem látszik, mert ezek nem módosíthatók. Ha viszont a függvény egy átadott listát helyben módosít (append, lista[i] = …), a változás a hívónál is megjelenik. Ha a paraméterhez új listát kötünk (pontok = pontok + [5]), az már egy másik lista, a hívóé változatlan marad. Kövesd lépésenként, és figyeld a nyilakat:

def bonusz_helyben(pontok):
    pontok.append(5)

def bonusz_uj_listaval(pontok):
    pontok = pontok + [5]
    return pontok

eredmenyek = [10, 20]
bonusz_helyben(eredmenyek)
print(eredmenyek)
uj = bonusz_uj_listaval(eredmenyek)
print(eredmenyek, uj)

Ha egy függvénynek nem szabad megváltoztatnia a kapott listát, ne módosítsa helyben: építsen új listát (vagy dolgozzon egy másolaton: lista.copy()), és azt adja vissza.

Mit ír ki?

x = 10

def f(x):
    x = x * 2
    return x

y = f(x)
print(x, y)
  1. 20 20
  2. 10 10
  3. 10 20

A függvény x paramétere egy helyi név, amely a hívásnál a 10-et kapja. A függvényen belüli x = x * 2 csak ezt a helyi nevet köti a 20-hoz; a fő program x-e 10 marad. A függvény a 20-at adja vissza, ez kerül az y-ba.

7. fejezet

Dokumentálás: a docstring

A függvény törzsének első sorában álló szöveg a docstring (dokumentációs szöveg). Leírja, mit csinál a függvény, mit vár és mit ad vissza. A help() függvény és a fejlesztőkörnyezetek ezt mutatják meg, amikor valaki a függvényedet használja.

def erdemjegy(pont, maximum=100):
    """Visszaadja a pontszámhoz tartozó érdemjegyet (1–5).

    pont: az elért pontszám (0 és maximum között)
    maximum: az elérhető pontszám, alapból 100
    """
    szazalek = pont / maximum * 100
    if szazalek >= 85:
        return 5
    if szazalek >= 70:
        return 4
    if szazalek >= 55:
        return 3
    if szazalek >= 40:
        return 2
    return 1

print(erdemjegy(78))
print(erdemjegy(30, 40))
help(erdemjegy)

A maximum=100 alapértelmezett érték: ha a hívó nem adja meg, 100 lesz. Részletesen a 9. modulban.

8. fejezet

Program függvényekből

Nagyobb programot érdemes úgy felépíteni, hogy a részfeladatok külön függvényekbe kerülnek, a fő program pedig csak „vezényel”: beolvas, hív, kiír. A függvények is hívhatnak más függvényeket. Kövesd lépésenként, hogyan rakódnak egymásra a keretek, amikor a prim_e belülről hívja az oszthato-t.

def oszthato(szam, oszto):
    return szam % oszto == 0

def prim_e(szam):
    """True, ha a szám prím; az 1 és a 2-nél kisebb számok nem prímek."""
    if szam < 2:
        return False
    oszto = 2
    while oszto * oszto <= szam:
        if oszthato(szam, oszto):
            return False
        oszto += 1
    return True

hatar = int(input("Meddig keressek prímeket? "))
primek = []
for n in range(1, hatar + 1):
    if prim_e(n):
        primek.append(n)
print(primek)

A „prím-e az 1?” kérdésre van válasz: nem. Ezért a prim_e() 2-nél kisebb számra False-t ad, nem hibát. Egy kérdező függvény lehetőleg minden értelmes bemenetre válaszoljon, és csak valóban értelmetlen bemenetnél jelezzen hibát.

9. fejezet

Gyakorló feladatok

Ezeknél a feladatoknál a gép a függvényedet közvetlenül, többféle argumentummal hívja meg, és a visszaadott értéket ellenőrzi. Ügyelj a függvény nevére és a paraméterek sorrendjére – pontosan úgy, ahogy a feladat kéri.

A nagyobbik

Írj nagyobb(a, b) nevű függvényt, amely két szám közül a nagyobbat adja vissza (egyenlőség esetén bármelyiket). A max() függvényt ne használd.

def nagyobb(a, b):
    # ide jön a megoldás
    pass

print(nagyobb(3, 8))
print(nagyobb(-2, -7))
def nagyobb(a, b):
    if a > b:
        return a
    return b

print(nagyobb(3, 8))
print(nagyobb(-2, -7))
Tipp

Egy if, két return. A pass sort cseréld le a megoldásra.

Kör kerülete és területe

Írj két függvényt: kor_kerulet(r) a kör kerületét (2rπ), kor_terulet(r) a területét (r²π) adja vissza. A π értékéhez használd a math.pi-t.

import math

# írd ide a két függvényt

print(f"{kor_kerulet(2):.2f}")
print(f"{kor_terulet(2):.2f}")
import math

def kor_kerulet(r):
    return 2 * r * math.pi

def kor_terulet(r):
    return r ** 2 * math.pi

print(f"{kor_kerulet(2):.2f}")
print(f"{kor_terulet(2):.2f}")
Tipp

A hatványozás **. A math.pi a modul egy értéke (nem függvény), ezért nincs utána zárójel.

Prímteszt függvényként

Írj prim_e(n) függvényt, amely True-t ad vissza, ha n prímszám, egyébként False-t (2-nél kisebb számokra is!). Ezután a fő program írja ki a prímszámokat 1 és 50 között egy sorban.

2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
def prim_e(n):
    if n < 2:
        return False
    oszto = 2
    while oszto * oszto <= n:
        if n % oszto == 0:
            return False
        oszto += 1
    return True

for szam in range(1, 51):
    if prim_e(szam):
        print(szam, end=" ")
print()
Tipp

A 3. modul prímes feladata itt függvényként tér vissza. A függvényből return False-szal azonnal kiléphetsz, amint találsz osztót.

Biztonságos átlag

Írj atlag(szamok) függvényt, amely egy számlista átlagát adja vissza. Üres listára ne adjon hibát, hanem None-t adjon vissza.

def atlag(szamok):
    return sum(szamok) / len(szamok)

print(atlag([4, 5, 3]))
print(atlag([]))
def atlag(szamok):
    if len(szamok) == 0:
        return None
    return sum(szamok) / len(szamok)

print(atlag([4, 5, 3]))
print(atlag([]))
Tipp

A kezdő kód üres listán nullával osztana. Előbb vizsgáld meg a hosszát, és ha 0, térj vissza None-nal.

Számjegyösszeg és ismételt számjegyösszeg

Írj szamjegyosszeg(n) függvényt (nemnegatív egész n számjegyeinek összege), majd gyok(n) függvényt, amely a számjegyösszeget addig ismétli, amíg egyjegyű számot nem kap – és ehhez a szamjegyosszeg-et hívja! Például gyok(9875): 9875 → 29 → 11 → 2.

def szamjegyosszeg(n):
    osszeg = 0
    while n > 0:
        osszeg += n % 10
        n //= 10
    return osszeg

def gyok(n):
    while n >= 10:
        n = szamjegyosszeg(n)
    return n

print(szamjegyosszeg(9875), gyok(9875))
Tipp

A gyok-ban: amíg n >= 10, legyen n = szamjegyosszeg(n). A függvényben az n = … értékadás csak a helyi n nevet köti új értékhez – a hívó változóját nem érinti.

Legkisebb és legnagyobb egyszerre

Írj min_max(lista) függvényt, amely egyetlen bejárással megkeresi a lista legkisebb és legnagyobb elemét, és a kettőt egy tuple-ben adja vissza: (legkisebb, legnagyobb). A min() és max() függvényt ne használd! Feltételezheted, hogy a lista nem üres.

print(min_max([4, -2, 9, 0]))   →   (-2, 9)
def min_max(lista):
    legkisebb = lista[0]
    legnagyobb = lista[0]
    for x in lista:
        if x < legkisebb:
            legkisebb = x
        if x > legnagyobb:
            legnagyobb = x
    return legkisebb, legnagyobb

print(min_max([4, -2, 9, 0]))
Tipp

Mindkét „eddigi legjobb” az első elemmel indul. A ciklusban két összehasonlítás kell. Írhatsz két if-et, de az elif is helyes: egy elem nem lehet egyszerre kisebb az eddigi legkisebbnél és nagyobb az eddigi legnagyobbnál. A végén: return legkisebb, legnagyobb.

Javítsd ki: print helyett return

Az alábbi brutto(netto) függvénynek a nettó árból a 27%-os áfával növelt bruttó árat kellene visszaadnia, egész forintra kerekítve (round()). Most azonban csak kiírja, ezért a program hibával leáll. Javítsd ki a függvényt úgy, hogy a fő program változatlanul működjön!

Összesen: 3175 Ft
def brutto(netto):
    print(round(netto * 1.27))

osszesen = brutto(1000) + brutto(1500)
print(f"Összesen: {osszesen} Ft")
def brutto(netto):
    return round(netto * 1.27)

osszesen = brutto(1000) + brutto(1500)
print(f"Összesen: {osszesen} Ft")
Tipp

Egyetlen szót kell kicserélni. Kövesd lépésenként a kezdő kódot: mit kap a hívás helyén az osszesen kifejezés?

10. fejezet

Összefoglalás

FogalomLényeg
def nev(p1, p2):definíció: a törzs csak híváskor fut
paraméter / argumentuma fejléc nevei / a híváskor átadott értékek; a sorrend párosítja őket
return xvisszaadja az értéket a hívás helyére, és befejezi a függvényt
nincs returnaz eredmény None; a print nem visszaadás!
return a, btuple-t ad vissza; kicsomagolás: x, y = f()
helyi változócsak a hívás keretében él; a global-t kerüljük
docstringa törzs első szövege; a help() mutatja

Gyakorold tovább: írd át a 2. modul számológépét függvényekre: szamol(a, muvelet, b) adja vissza az eredményt (nullával osztásnál None-t), a fő program pedig csak beolvas, hív és kiír. Melyik rész hová került, és mit nyertél vele?