Bevezetés a Python nyelvbe#

Python egy álatlános célú, magas színtű, gyengén típusos, interpretált programozási nyelv. Nézzük meg egyesével, mit jelentenek ezek a jelzők.

  • Általános célú, így számos különböző célra használható a web applikációktól kezdve, tudományos kutatáson át, különböző szkript feladatok elátására.

  • A nyelv magas színtű, mivel a Python magas szintű absztrakciót nyújt a gépi kódhoz képest. Ezzel szemben a Matlab főleg mérnöki és kutatási feladatokra használható jól.

  • Gyengén típusos, mivel nem szükséges előre definiálni az egyes változók típusát, így a változó deklarálás és inicializálása nem válik tisztán szét. A változók típusa futás időben, dinamikusan döl el.

  • Interpretált, ugyanis a Python kód önmagában nem futtatható, szükséges hozzá egy interpreter, ezért általánosságban elmondható, hogy a Python kód lassabban fut, mint egy olyan nyelv kódja, mely gépi kódra fordul (pl. C vagy C++).

Fordító program és interpreter

Kiegészítő anyag

Megjegyezzük azonban, hogy nem minden esetben érzékelhető a Python nyelv lassúsága. Bizonyos esetekben azt tapasztalhatjuk, hogy a Python kód gyorsabban fut mint egy álatlunk írt C kód. Ez azért lehetséges, mert számos Python könyvtár mögött direketen alacsonyabb szintű nyelven írt kódok futnak, amikor bizonyos műveleteket, függvényeket hívunk; ilyen például különboző mátrix műveletek a numpy könyvtárban. Nagy méretű mátrix szorzás Pythonban nagy valószínűséggel gyorsabban fog futni, mintha mi probálnánk azt C nyelvbe megírni valamilyen könyvtár használata nélkül. Továbbá, érdemes tudni, hogy a Python képes arra, hogy futás időben lefordítsa a Python kódot gépi kódra (Just-In-Time fordítás), de annak futás ideje és memória kezelése még mindig elmaradhat egy alacsony nyelv implementációjától.

A Python programozási nyelvet egy holland programozó Guido van Rossum alkotta meg az 1990-es évek elején. A nyelvnek két nagy verziója van. A Python 2, a régebbi, amellyel még találkozhatunk elvétve. Python 3-t 2008-ban mutatták be. A Python 3 nem kompatibilis a 2-es verzióval, így fontos tisztában lennünk a két verzió közötti különbségekkel. Jelenleg a 3.8-as verzó a legfrissebb, melyet 2023-ban adtak ki. Habár a nyelv története 3 évtizedre nyúlik vissza, népszerűsége az elmúlt 5-10 évben kezdett emelkdeni. Mára a Python az egyik legkedveltebb és a munkaerő piacon at egyik legkeresetebb nyelvek közé tartozik. A mesterséges intelligencia, mélytanulási algoritmusok és adat tudományok elsődleges nyelve. Könnyű portolhatósága miatt számos felhasználó programba beépült, és ezek a programok Python nyelven nyújtanak hozzáférést a programozási felületükhöz, vagy szak zsargonban API-juk (Application Programming Interface) hoz, így péládul QGIS. Gyakran találkozhatunk a Pythonnal szervereken, ahol különböző backend logikát valósít meg.

Kezedtbe a nyelvet úgy tervezték, hogy kezdők is gyorsan el tudják sajátítani, illetve kikényszerítsen helyes kódolási technikát (lásd identálás). Későbbiekben fontos szempont volt a nyelv egyszerűsége, és hogy a programozó az eredményre tudjon koncentrálni, és ne a nyelv különböző elemeire keljen fókuszálni; így a nyelv különösen alkalmas ötletek kipróbálására, prototipizálásra. A számos, sok funkciójú, jó minőségű és nyílt (ingyenesen elérhető) programcsomagok további érvet nyújt ahhoz, hogy ezt a programnyelvet válasszuk. Ki szeretném hagsúlyozni, a legtöbb csomag ingyenességét: felhasználói, és sok esetben kereskedelmi célra is elérhetőek ezek, beleértve a legtöbb Python ekoszisztémába tartozó fejlesztői eszközt.

python_popularity.png

Kiegészítő anyag

Nyílt forráskód (open source), tehát hogy bele tudok nézni a forrás kódba, nem feltétlenül jelenti a program teljes körű ingyenességét. Nyílt és ingyenesség kérdése két különböző definíció, habár nyílt forráskódú programok gyakran bizonyos szempontból ingyenesek. “Ingyenességnek” is lehetnek különböző fokozatai: ingyenes a felhasználása kereskedelmi célra, vagy csak nem kereskedelmi (pl. akadémiai, hobbi) célú használata engedélyezett, engedélyezett-e a terjesztése, beépíthető-e a könyvtár kereskedelmi termékékbe és ha igen azt jeleznünk kell-e. Ezek miatt számos licensz kvázi szabvány létezik, melyek pl. MIT, BSD, LGPL, GPL, FOSS. Mindig bizonyosodjunk meg arról, hogy a könyvtárakat, eszközöket licenszének megfelelően használjuk. Mindazonáltal érdemes megjegyezni, hogy a mérnöki szoftverekkel ellentétben, az IT iparban, számos a közösség által fejlesztet eszököz, melyek rendkívűl megengedő licenszeléssel vannak kiadva, napi szinten vannak használva.

A Python, mivel interpretált nyelv, ezért szükséges a Pyton futtató környezet telepítése az operációs rendszerünkre; ez online letölthető. Maga az interpreter egy konzol alkalmazás; a forráskódot egy ASCII szöveg szerkesztőben írjuk, és a kódot konzolon kersztül adjuk át a Python interpreternek. Az alábbi képernyőkép a Python interpretert mutatja egy Ubuntu Linux konzolban.

Python konzolban

A Pythonhoz tartozó ökoszisztéma igen bő, mely magába foglal számos fejlesztői környezetet és csomagkezelőt. Ezek között említünk meg néhányat:

  • pip csomagkezelővel a különböző prgramocsomagokat tudunk telepíteni

  • A Spyder fejlesztői környezet (Anaconda tartalmazza) egy a Matlabban megszokott felületet kapunk.

  • IPython (Interactive Python) egy “kernel”, mely lehetővé tesz interaktív számításokat.

  • Jupyter Notebook egy web alapú notebook környezetet szolgálatat az IPython kernelhez a Mathematica vagy Matlab notebookjaihoz hasonlóan.

  • Az Anaconda ingyenes környezet segítségével egyszerűen telepíthetünk 250 olyan programot, amelyet mérnöki, tudományos számításokhoz használhatunk. Anaconda telepítésével megkapjuk a Spyder és Jupyter Notebook progarmokat is. Egyszerű telepítése miatt ajánlott kezdő felhasználóknak.

  • Visual Studio Code az egyik legnépszerűbb általános célú ingyenes fejlsztői környezet, melyhez Python kiegészítő is tartozik. Hatékonyan használható Python fejleszésre. A Python kiegészítő IPython interfészt is biztosít.

Spyder felhasználói felülete:

Spyder felhasználói felülete

Visual Studio Code felhasználói felülete:

Visual Studio Code felhasználói felülete

Colab környezet#

Colab egy a Google által nyújtott, részben ingyenes, IPython kernelt futtató Jupyter Notebook típusú felhő szolgáltatás. Bejelentkezéshez Google email fiókkal kell rendelkeznünk, mely ugyancsak ingyenesen létrehozható. Mivel ez egy felhő szolgáltatás, a kódok végrehajtása nem a saját számítógépünkön, hamen a Google által üzemeltetett szerveren történik: a böngészőnkben csupán a kódot írjuk.

A kódot a Colab webes felület celláiban adhatjuk meg. A kódot cellákba renszerezzük. Ezután a cellákat tudjuk külön-külön futtatni:

  • Egyenként futattatni a cellákat shift + Enter vagy a ▶ gombra kattintva lehet. Valamennyi időbe beletelhet a kernel elindítása, lásd lejjebb.

  • Minden cellát futtatni fentről lefele: RuntimeRun all

  • Kernel újraindítása: RuntimeRestart runtime

A kód futtatása a következő képpen zajlik a háttérben:

  1. A cella elküldésre kerül a Google szerveihez, majd

  2. a szerveren egy dedikált kernelen a kód lefut,

  3. ezután a futás eredménye vissza küldésre kerül a webböngészőnkbe.

A Python kernel dedikált az éppen aktuális notebookhoz, ezért a létrehozott változók, fájlok megmaradnak a cellák közötti futtatások során. A kernel újraindítása szükséges ahhoz, hogyha a korábban létrehozott változóinkat szeretnénk töröljni, és ha egy friss kernellel szeretnénk újrakezdeni. Az első cella futtatása hosszabb ideig eltarthat, mivel a szerver ilyenkor indítja el a kernelt. Amennyiben hosszabb ideig nem végzünk interakciót a Colabban, a szerver lezárja a kapcsolatunkat és leállítja a kernelt; ekkor minden változónk, fájlunk elvész.

Colab architektúrája

Tipp

Ha valami hibát kapunk, az jó eljárás, hogy eltöltünk 1-2 percet a hiba keresésével, hátha valami triviális. Ha nem megy akkor, indítsuk újra a kernelt és futassuk újra az összes cellát. Ez egy menü elemmel is elérhető: RuntimeRestart and run all. Ha a hiba ezek után is fent áll, akkor további hibakeresésre van szükség.

Figyelem

Ha hosszabb ideig nem végzünk műveleteket a Colab környezetbe, akkor a kernel a háttérben már leállt, ezért minden eddigi számításunk elveszett. Ilyenkor szükséges az összes cella újbóli futattása a RuntimeRun all menüvel

Hello world!#

A “Hello world!” program egy olyan kód, ami a címben szereplő szöveget írja ki a képernyőre. Ez egy olyan egyszerű program ami demonstrálja, hogy a fordító/fejlesztési környezet készen áll a használatra. További információ és példa “Hello world!” programok a wikipédián található.

Írjuk meg legelső Hello world programunkat, és próbáljuk meg értelmezni!

print("Hello world!")
Hello world!

Változók#

Változó: Egy Exceles analógiát véve, a változókat úgy képzelhetjük el mint a cellákat az Excel-ben: valamilyen értékeket hozzá tudunk rendelni egy-egy változóhoz, majd azokon műveleteket tudunk végrehajtani. A különbség itt, hogy míg Excelben a $ jellel jelöljük meg a cellákat, itt erre nincs szükség.

Figyelem

A változókat mindig szöveges karakterrel kezdődik, majd ezt követően tartalmazhat ékezet nélküli betüket, számokat, és kevés speciális karaktereket. Általában az alábbi speciáls karaktereket szoktuk használni változó nevekben:* _, $. Törekedjünk értelemes változó nevek megadására, így könnyebben olvasható a kód.

Tipp

Jó ökölszabály arra gondolni, hogy olyan változónevet válasszunk, amelyet felhasználónévként is megadhatnánk egy weboldal regisztrációja során.

A következő példa mutatja, hogyan rendelhetünk értéket egy változóhoz. Az Exceles analógiával élve, ezt úgy képzelhetjük el, hogy a művelet megfelel az Excel cellába történő valamilyen érték megadásának.

pi = 3.21
hello_world_string = "Hello world!"
data_1 = 5321

Kiegészítő anyag

Mivel spacet nem használhatunk változó nevekben, ezért a gyakorlatban az öszetett szavakból álló változó nevekhez különböző gyakorlatok terjedtek el.

# Pascal case konvenció:
PascalCaseVariable=1

# Camel case konvenció:
camelCaseVariable = 1

# Snake case konvenció:
snake_case_variable = 1

Egyszerű típusok#

Az egyszerű típusok, olyan típusok, amelyekből más típusok felépíthetőek, vagy rendszerezésükkel más összetett adattípusok vezethetőek le. Az egyszerű típusokat a programozási nyelv alapértelmezetten definiálja, így bármilyen könyvtár használata nélkül elérhetőek. Megjegyezzük, hogy ez nem zárja ki azt, hogy összetett adattípusokat a nyelv alapértelmezetten definiáljon, mint ahogy azt később látni fogjuk a listák, szótárak esetén. Pythonban egyszerű típusok például az egész, lebegőpontos szám, a szöveg, vagy a logikai érték (boolean). Ahogy az Excelben a celláknak, úgy a változóknak is van típusa, pl. dátum, szöveg, szám.

Példa egész szám definiálása:

test_int = 1
print(test_int)
print(type(test_int))
1
<class 'int'>

A fenti kódban, először az 1-t értékül adjuk a test_int voltozónak, majd annak értékét kiírjuk. A type(...) függvény megadja a változó típusát, amit a kiírásnál az utolsó sorban láthatunk.

Lehetőségünk van egyidejüleg több változónak is az értékét meghatározni az alábbi módon:

a, b, c = 1, 2, 3

print(a)
print(b, c)
1
2 3

Kiegészítő anyag

Erősen típusos nyelveknél a válozók értékadása két elkülünölő lépésből áll: a változó deklarálásából és annak inicializálásából. A deklarálás alatt a változó típusát adjuk meg, inicializáláskor pedig megadjuk annak kezőértékét. Nézzünk egy példát C++ nyelven: int a = 1; Itt az a változó int (egész) típusát deklaráljuk, majd a változó értékének 1-t adunk. Az a változó típusa ezután nem változhat meg: annak mindenkor valamilyen egész típust kell tárolnia. Gyengén típusos nyelvek esetén - amilyen a Python is - a deklarálás és inicializálás nem válik el élesen. A változó típusa az inicializáláskor megadott értéket veszi fel, mely típus futás során dinamikusan bármikor megváltozhat.

Lebegőpontos szám definiálása:

test_float = 0.13242
print(test_float)
print(type(test_float))
0.13242
<class 'float'>

Láthatjuk, hogy a változó float, azaz lebegőpontos (tizedestört) számot vett fel.

Figyelem

A tizedes jel a programozási nyelvek világában a pont . nem pedig a vessző ,. Ez egy fontos különbség az Excelhez képest, ahol a lokalizációs beállításoktól függően, lehetőségünk van tizedes vessző használatára. A pont használatának oka, hogy a programozási nyelveket elsődlegesen az angolszász világ terjesztette el, és a angol nyelv a tizedes pontot használja. Ugyancsak vegyük észre, hogy nem áll módunkban az ezresek jelőlése sem ponttal, sem pedig vesszővel.

Egy másik fontos egyszerű típus a logikai érték (bool):

test_logical = True
print(test_logical)

test_logical = False
print(test_logical)
print(type(test_logical))
True
False
<class 'bool'>

Értelmezzük a következő kifejezést! Vegyük észre hogy a == összehasonlítást, míg az = az értékadást jelentő egyenlőség jelek. Az összehasonítás eredménye True vagy False, azaz igaz vagy hamis érték lehet, aminek a típusa bool. Az alábbi kód a False értéket fogja kiírni, mivel a 1 == 2' kifejezés értéke False, amit értékül adunk a test_logic` változónak.

test_logical = (1 == 2)
print(test_logical)
False

Ezzel szemben az alábbi kód a True értéket fogja kiírni, mivel a 1 == 1 kifejezés értéke True, amit értékül adunk a test_logic változónak.

test_logical = (1 == 1)
print(test_logical)
True

Pythonban elérhetőek logikai műveleteket, mint például and, or, not:

print(not True)
print(True or False)
print(True and False)
print(False and True)
False
True
False
False

Logikai műveletek eredményeit igazságtáblákba foglalhatjuk. Néhány gyakran használt szabály:

  • or kifejezés akkor igaz ha valamelyik operandus igaz, és

  • and kifejezés akkor igaz ha mindkét operandus igaz.

Hasonlóan a valós számok körében megismert azonossádokhoz, a logikai műveleteken esetén is ismert számos azonosság. Például ellenőrízzük le De Morgan azonosságot:

print((not (True and False)) == (not True or not False))
True

Feladat

Gyakorlásképpen nézzük meg mit ír ki a következő kód!

a = 1
b = 2
test_logical = (a == 1) or not (a == b)
print(test_logical)

Másik gyakran használt egyszerű típus a szövek. Szöveg definiálása dupla vagy szimpla aposztrófok között lehetséges a következő módon:

variable = "hello"
print(variable)
print(type(variable))

variable = 'hello'
print(variable)
print(type(variable))
hello
<class 'str'>
hello
<class 'str'>

Tipp

Érdemes a hibaüzeneteket tanulmányozni, sokat segíthet kódolási hibák megtalálásában.

De ' és " egymásba ágyazhatóak, mint például:

print('Az mondta: "menj tanulni!"')
Az mondta: "menj tanulni!"

Figyelem

Az alábbi kód azonabn hibás, mert ' és " keverve van, nem pedig egymásba ágyazva:

variable = 'hello" # SyntaxError: EOL while scanning string literal

Műveletek egyszerű típusokkal#

Műveleteket változók között definiálhatunk. Unáris műveleteken, olyan műveleteket értünk, melynek egy operandusa van, pl. abszolút érték művelete. Bináris műveletnek két operandusa van, a leggyakrabban használt műveletek ilyenek, pl. összeadás, szorzás.

Szám típusú változókkal történő matematikai műveletek úgy működnek, ahogy ezt máshol is megszokhattuk, pl. Excelbe.

pi = 3.14
r = 5.0
K = 2.0*r*pi
print('K értéke:', K)
K értéke: 31.400000000000002

A műveletek kiértékelése balról jobbra történik a zárójelek és a matematikában megszokott precedenciák (műveleti sorrend) szerint. Törtek esetén különösen figyeljünk a matematikában megszokott kifejezések helyes kódolására. Erre példaként nézzük a \(\dfrac{a}{b*2}\) kifejezést:

b = 2.0
a = 2.0

print(a/b*2)     # nem az eredeti tört kifejezés
print(a/(b*2))   # helyesen
2.0
0.5

Az összeadás művelete szövegek esetén is értelmezett. Ekkor a szövegek összefűzéséről, vagy konkatenációjáról beszélhetünk, amikor két szöveg egy olyan új szöveggé válik, amely a bal oldali majd jobb oldali szövegek karaktereit tartalmazza:

hello_world = "Hello " + "world!"
print(hello_world)
Hello world!

És egy másik példa a függvény parametér listájában megadott műveletre:

name = "Joe";
hello = "Hello"
print(hello + " " + name + "!")
Hello Joe!

A fenti kódrészlet utolsó sorát a következő képpen kell olvasni:

  1. először a hello + " " + name + "!" kifejezés balról jobbra kerül végrehajtásra, mely az egyes szöveg típusok konkatenációjat jelentik. Így egy köztes érték jön létre, mely a Hello Joe! szöveg,

  2. majd a köztes érték kerül a függvénynek átadásra. A függvényekről, még később bővebben lesz szó.

Ezután nézzük meg a hatványozást Pythonban, melynek műveleti jele a **:

T = r**2.0*pi
print(T)
78.5

Hatványozással négyzet gyök is kifejezhető. Az alábbi példában, egy új alapértelmezetten definiált típust is láthatunk, amikor a -9 negyzetgyökét vonjuk:

a = -9
print(a**(1/2))
(1.8369701987210297e-16+3j)

Feladat

Egy példa, melyben minden alapművelet szerepel. Mit csinál az alábbi kód? Mit fog kiírni a kód?

a, b, c = 2, 0, 0
x1 = (-b + (b**2 - 4*a*c)**0.5) / (2*a)
x2 = (-b - (b**2 - 4*a*c)**0.5) / (2*a)
print(x1 == x2)

Típus konverziók#

Nagy általánosságban, bináris műveleteket csak azonos típusok között értelmezzünk, melynek eredménye a kérdéses típus lesz. A fenti példában nézzük meg a változók típusát:

pi = 3.14
print('pi típusa:', type(pi))

r = 5.0
print('r típusa:', type(r))

K = 2.0*r*pi
print('K értéke:', K)
print('K típusa: ', type(K))
pi típusa: <class 'float'>
r típusa: <class 'float'>
K értéke: 31.400000000000002
K típusa:  <class 'float'>

Amennyiben a két típus nem azonos, akkor típuskonverzióra van szükség.

Figyelem

Nézzünk meg egy példát, amely során int típust akarunk str típussal összeadni. Az alábbi kód hibával fog leállni:

age = 22
message = age + " éves vagyok" # Error: TypeError: unsupported operand type(s) for +: 'int' and 'str'
print(message)

Hibát kapunk, mivel, habár egészek és szövegek között az összeadás művelete értelmezett, de egész és szöveg között nem. Nézzük meg az összeadásban resztevevő opreandusok típusát:

age = 22
print('bal operandus: ', type(age))
print('jobb operandus: ', type(" éves vagyok!"))
bal operandus:  <class 'int'>
jobb operandus:  <class 'str'>

Tehát típuskonverzióra van szükség. Valamilyen típust szöveggé az str() függvény segítségével tudunk szöveg típussá alakítani:

age = 22
print('age típusa:', type(age))

age_str = str(age) # <-- típus konverzió az  str(..) függvény segítségével
print('age_str típusa:', type(age_str))

message = age_str + " éves vagyok!"
print('message értéke:', type(age_str))
age típusa: <class 'int'>
age_str típusa: <class 'str'>
message értéke: <class 'str'>

Vagy röviden:

age = 22
message = str(age) + " éves vagyok!"
print(message)
22 éves vagyok!

Feladat

Valamilye típust egésszé az int(), valamint lebegő pontossá a float() függvényekkel konvertálhatunk. Nézzük meg mit ír ki az alábbi kód. Mi történik a float típussal amikor int típusra konvertálódik?

pi = 3.1415
print('val_float értéke: ', pi)
print('val_float típusa: ', type(pi))

pi_int = int(pi) # típuskonverzió float -> int

print('val_float értéke: ', pi_int)
print('val_float típusa: ', type(pi_int))

Nézzük meg a következő példát:

test_float = 1.2
test_int = 1
test_result = test_float * test_int
print(test_result)
1.2

Nézzük meg a műveletben résztvevő változók típusát:

test_float = 1.2
print(type(test_float))

test_int = 1
print(type(test_int))

test_result = test_float + test_int
print(type(test_result))
<class 'float'>
<class 'int'>
<class 'float'>

Láthatjuk, hogy az összeadás bal oldalán lebegőpontos, jobb oldalán egész típus található, az eredmény pedig lebegőpontos. Talán elsőre triviális, de vegyük észre, hogy a háttérben típus konverzió történik. Ez a típus konverzió automatikus, ezért automatikus típuskonverzióról beszélünk. A következő lépések történnek a háttérben, amikor a test_result = test_float + test_int művelet végrehajtódik:

  1. Mivel a szorzás két oldán különböző típus szerepel, a test_int változó int típusa float típussá konvertálódik.

  2. Ez a közbenső float típussú érték kerül szorzásra a test_float float típusú változóban található értékkel.

  3. Mindkét oldalon float típus található, így az eredmény is float lesz, amely a test_result változóba kerül.

Más szavakkal a következő kód fog lefutni:

test_result = float(test_float) + test_int

Egy másik kód részlet, amely a kör kerületét adja meg:

pi = 3.14
r = 5
K = 2*r*pi
print(type(K))
print(K)
<class 'float'>
31.400000000000002

Itt az r sugár int típusként van definiálva, melynek értéke a szorzás művelet során float típusúvá válik. Vegyük észre, hogyha a pi float típus kerülne int konverzióra, akkor rossz eredményt kapnánk, hiszen a tizedes értékek levágásra kerülnének. Elmondható tehát, hogy automatikus típuskonverzió esetén a kisebb halmazú típus kerül a nagyobb halmazú típusra konvertálásra.

A kód szintaktikája és szemantikája#

Szintaktika alatt a programozási nyelvet leíró “nyelvtant”, szabályrendszert értjük, például milyen elnevezése lehet egy változónak, vagy hogy hogyan definiálunk értékadást, függvényeket. Ezzel szemben a szemantika alatt a program “értelmét” értjük, vagyis azt hogy helyesen működik-e vagy sem.

Nézzünk egy példát az elő nyelvből:

A ég kék.

Ez a mondat helytelen, mivel az “ég” határozott névelője “az”. Ez egy szintaktikai hiba.

Az ég piros.

Ez a mondat nyelvtanilag helyes, tehát szinaktikai hiba nincs. Ellenben az ég nem piros; ez szemantikai hiba.

Ökölszabály, ha a kód szintaktikailag nem helyes, akkor mindenképpen hibaüzenetet kapunk. Valamilyen fejlesztő eszközt használva, az automatikus kódelemzők a legtöbb esetben már futás előtt jelzik a hibát. Nem interpretált nyelvek esetén - tehát olyan programozási nyelvek esetén, ahol szétválik a fordítás és a futtatás - a szintaktikai hiba már fordítási időben kiderül. Ezzel szemben, egy szemantikailag helytelen program esetén a hibára fordítási időben nem derül fény. Jó példa szemantikai hibára a tömbök túlcímzése, vagyis amikor egy tömb vagy vektor olyan elemére hivatkozunk, amely túlmutat a tömb hosszán.

Függvények#

Függvényekkel már találkoztunk korábban, ilyen volt a print. A print egy alapértelmezetten definiált függvény, azonban saját magunk is írhatunk függvényeket.

A függvények - hasonlóan a matematikában megtanultakhoz - valamilyen bemeneti értékre (független változó) adnak valamilyen kimenetet (függő változó). Azonban, szemben a matematikai függvényekkel, a programozási nyelvek függvényeinek nem feltétlenül van bemeneti paramétei (argumentumai), illetve nem feltétlenül van kimenetük (visszatérési érték). Függvényekre gyakran eljárásként hivatkoznak. A függvények egy speciális esete, az olyan függvény, melynek nincs bemeneti paramétere; ekkor használhatjuk a metódus elnevezést a függvényre.

Egy függvény komplex dolgokat oldhat meg, melynek során további függvényeket is meghívhat. A függvények célja hogy logikailag összetartozó kódot egy egységbe foglaljanak, így növelje a kód újrafelhasználhatóságát és olvashatóságát. Vegyük észre, hogy a függvény használója számára nem érdekes hogy egy bizonyos feladatot a függvény hogyan old meg, csupán az, hogy mik a bemeneti paraméterei és kimeneti értékei. Így a feladat megoldásának mikéntje a függvény feladata. A függvény bemenetét leíró paraméter listát és a visszatérési értéket együtt a függvény szignatúrájának hívjuk.

A Python nyelvben a függvények szintaktikája a következő:

def hello(name):
  my_string = 'Hello ' + name + "!"
  print(my_string)

Feladat

Elemezzük a fenti kódot és definiáljuk a függvény szintaktikáját! Azonosítsuk a következőeket:

  • bemeneti paraméter

  • függvény neve

  • függvény törzse

Vegyük észre, hogy a fenti kód részletben a függvény törzsének definiciója be van húzva spaceszel vagy tabbal (ezeket közös néven whitespace karaktereknek hívjuk). Ezeket a whitespaceket hívjuk rossz magyarsággal indentálásnak. Pythonban a sor eleji whitespace karakterek fontos jelentőséggel bírnak, hogy megkülönböztessünk függvény törzs, illetve más vezérlési szerkezetek belső részét. Mindig figyelj oda, hogy a sor eleji whitespacek megfelelőek legyene, máskülönben hibát fogsz kapni.

Figyelem

Példaként nézzünk meg néhány identálási hibát:

def hello(name):
    name = 'Anna'
   print(name)       # hiba: egy szóköz hiányzik 
 hello('Anna')       # hiba: egy szóköz felesleges

# Helyes identálással:
def hello(name):
    name = 'Anna'
    print(name)       
hello('Anna')       

Az így definiált függvényt más cellákban is meg tudjuk hívni.

hello("Joe")
hello("Jane")
Hello Joe!
Hello Jane!

Ahogy említettük korábban, a függvényeknek lehet visszatérési értéke, melyet a return kulszóval adhatunk meg. A következő függvény egy kör területét számítja ki, és amelynek a bemeneti paramétere a kör sugara:

def circle_area(r):
  T = r**2*3.14145
  return T

Számítsuk ki az 5 sugarú kör területét:

r = 5
T = circle_area(r)
print("Egy " + str(r) + " sugarú kör területe: " + str(T))
Egy 5 sugarú kör területe: 78.53625

A python nyelveben egy függvénynek több visszatérési értéke is lehet, melyeket vesszővel elválasztva a return kulcsszó után adhatunk meg:

def circle_params(r):
  K = 2*r*3.14145
  T = r**2*3.14145
  return K, T

A függvény hívásakor, a többszörös visszatérési értékeket, a bal oldalon, vesszővel elválasztva adhatjuk meg:

K, T = circle_params(5)
print('A kör kerülete: ', K)
print('A kör területe: ', T)
A kör kerülete:  31.414499999999997
A kör területe:  78.53625

Függvénynek lehet ún alapértelmezett paramétere. A bemeneti paraméter alapértelmezett értékét függvény bemenetének megadásakor adhatjuk meg az egyenlőség jelet használva. Nézzünk egy példát:

def circle_area(r=1):
  T = r**2*3.14145
  return T

Az alapértelmezett paraméter, egy olyan bemeneti paraméter, amelyet nem kötelező megadni. Így például a circle_area függvényt paraméter nélkül is hívhatjuk:

print("Egység sugarú kör területe: " + str(circle_area()))
Egység sugarú kör területe: 3.14145

De lehetőség van a sugar megadására is, ha akarjuk:

print("Egy 5 sugarú kör területe: " + str(circle_area(5)))
Egy 5 sugarú kör területe: 78.53625

Feladat

A következőekben nézzünk egy példát egy komplex kódra, mely egy gyakran alkalmazott geodéziai számítást old meg. Vegyük észre, hogy a függvény egy jól körülhatárolt problémát old meg, melynek bemenetei és kimenetei tisztán adottak.

  • Milyen geodéziai problémát old meg az alábbi kód?

  • Mi a függvény szignatúrája? Mik a bemeneti és kimeneti paraméterek?

  • Mi a célja az alapértelmezett paraméternek?

  • Van-e sorrendi megkötés az alapértelmezett és nem alapértelmezett paramétereknek? Például felcserélhető-e a lan és ellipsoid paraméterek?

  • Teszteljük a kódot a BUTE állomás koordinátáival.

import math
def llh2ecef(lat, lon, alt, ellipsoid='wgs84'):
    rad_lat = lat * (math.pi / 180.0)
    rad_lon = lon * (math.pi / 180.0)

    if ellipsoid == 'wgs84':
      a = 6378137.0
      finv = 298.257223563
    else:
      print('Unknown ellipsoid')
      return
      
    f = 1 / finv
    e2 = 1 - (1 - f) * (1 - f)
    v = a / math.sqrt(1 - e2 * math.sin(rad_lat) * math.sin(rad_lat))

    x = (v + alt) * math.cos(rad_lat) * math.cos(rad_lon)
    y = (v + alt) * math.cos(rad_lat) * math.sin(rad_lon)
    z = (v * (1 - e2) + alt) * math.sin(rad_lat)

    return x, y, z

llh2ecef(47.4809437284, 19.0565297521, 180.811)
# Eredmény: (4081882.3780616224, 1410011.1419668908, 4678199.39132865)

Változók láthatósága és névterek (scope)#

Ahogy korábban beszéltünk róla, a függvények logikailag összetartozó kód sorokat fognak össze. A függvény használója számára nem érdekes az, hogy a program a feladatot hogyan oldja meg, ő csak a számításhoz szükséges paramétereket akarja átadni, és az eredményet megkapni. A függvényen belül azonban lehetőségünk van változók létrehozására, manipulálására. Azért, hogy ezek a változók a függvényen kívülről ne legyenek elérhetőek, a programozási nyelvek a változók számára láthatóságot, illetve hatókört definálnak. Ezzel elkerülhető, hogy változó névadási bonyodalmakba keveredjünk.

Nézzük meg az alábbi kódot:

name = 'Anna'       # globális névtér
def hello():        # globális névtér
   name = 'Béla'    # a függvény névtere
   print(name)      # a függvény névtere
hello()             # globális névtér
print(name)         # globális névtér
Béla
Anna

A fenti kódban a name változó a függvényen kívűl kerül definiálásra, majd egy másik name változó a függvényen belül. Két esetben írjuk ki a name-k értékét: egyszer a függvényen kívül az utolsó sorban, máskor a függvényen belül a hello() függvény hívásakor. Vegyük észre, hogy az eredmény sorrendje: Béla, Anna. Ha a függvényen belüli name = 'Béla' sort nézve a name változó a külső name változóra hivtkozna, akkor az utolsó sorban lévő printnek az Anna szöveget kéne kiírnia, hiszen a függvényen belül az megváltozott. De nem ez történik, mivel a két name változót, bár ugyanaz a karakterlánc reprezentálja, de azok mégis különböző változók. Különbözőségüket az adja, hogy más-más helyen vannak definiálva. A függvényen kívűl létrehozott változók, az ún. globális névtérben kerülnek létrehozásra. A függvény törzsében létrehozott változók csak a függvény névterében érhetőek el.

Feladat

Próbáljuk meg futtatni az alábbi kódot! Miért áll le a program hibával?

def hello():
  name_hello = 'Anna'
  print(name_hello)

hello()
print(name_hello) # hiba: NameError: name 'name_in_hello' is not defined

A fenti feladatban lévő kód kis változtatással lefut:

name = 'Anna'
def hello():
  print(name)

hello()
print(name)
Anna
Anna

A kód lefut, mivel a name változó a függvényen kívül került létrehozásra, a globális névtérben. A globális névtérben létrehozott változók elérhetőek a függvények belsejében.

Tehát a változók és névterekre vonatkozó szabályok a következőek:

  • A globális névtérben létrehozott változók mindenhol, még függvények belsejében is elérhetőek, ha azok a függvény definiálása előtt kerültek létrehozásra.

  • A függvények belsejében lévő változók csak az adott függvényen belül érhetőek el.

Végezetül, bár triviális, a függvény paramétere a függvény névteréhez tartozik. Nézzünk egy példát:

name = 'Anna'                    
def hello(name):                 
  print(name) 

hello('Béla')  
print(name) 
Béla
Anna

Feladat

Mit ír ki a következő kód és miért?

name = 'Anna'                      
def hello(name):                   
  name = 'Béla'                    
  print(name)     

print(name)
hello('Cecília')
print(name)

Feladat

Mit ír ki a következő kód és miért?

name = 'Anna'

def hello1():
  print(name)

def hello2():
  name = 'Béla'
  print(name) 

print(name) 
hello1()
hello2()
print(name)

Hívási verem#

Kiegészítő anyag

Ez a rész kiegészítő anyag és csak a függvényhívások mélyebb megértését segíti.

Hívási veremről további info: https://towardsdatascience.com/python-stack-frames-and-tail-call-optimization-4d0ea55b0542

Rekurziós függvények#

A rekurziós függvények olyan függvények, amelyek önmagkuat hívják. Klasszikus rekurziós példa a Fibonaci sorozat:

def recur_fibo(n):
   if n <= 1:
       return n
   else:
       return(recur_fibo(n-1) + recur_fibo(n-2))

recur_fibo(20)
6765
print(recur_fibo(1), recur_fibo(2), recur_fibo(3), recur_fibo(4), recur_fibo(5), recur_fibo(6), recur_fibo(7))
1 1 2 3 5 8 13

Feladat

Hozzunk példát más rekurziós függvényre!

Függvény könytárak és import#

A python egyik nagy erőssége, és az ok, hogy annyira népszerű, a kiterjedt és sokféle feladatra használható ingyenes függvénykönyvtárai. Leegyszerűsítve egy könyvtár hasonló változók és függvények gyűjteménye. Egy könyvtárat az import kulcsszóval tudunk betölteni:

import math

Egy könytáron belüli változóra vagy függvényre hivatkozni a könyvtár neve és a . jel segítségével tudunk, például:

print(math.pi)
3.141592653589793

A következő példa, a könyvtár használatát mutatja be egy korábbi függvényünkben. Itt a pi értékét nem mi definiáljuk, hanem a könyvtárból vesszük:

def circle_area(r=1):
  T = r**2*math.pi
  return T
print("Egység sugarú kör területe: " + str(circle_area()))
Egység sugarú kör területe: 3.141592653589793

Elhagyható a könyvtár nevére való hivatkozás, amennyiben a from ... import ... kulcsaszavakkal a függvényt direktbe importájuk:

from math import pi
print(pi)
3.141592653589793

Nézzünk példát egy másik könyvtár használatára:

import time

Egy könytáron belüli változóra vagy függvényre hivatkozni . segítségével tudunk, például:

t = time.localtime()
current_time = time.strftime("%Y/%m/%d %H:%M:%S", t)
print(current_time)
2024/05/02 09:51:17
t = time.localtime()
current_time = time.strftime("%Y év %m hónap %d nap %H óra %M perc %S mp", t)
print(current_time)
2024 év 05 hónap 02 nap 09 óra 51 perc 17 mp

Az as kulcszó segítségével átnevezhetjük a könyvtárat, így nem kell az eredeti hosszú névre hivatkozni:

import time as t
local = t.localtime()
current_time = t.strftime("%Y/%m/%d %H:%M:%S", local)
print(current_time)
2024/05/02 09:51:17

Elhagyható a könyvtár nevére való hivatkozás, amennyiben a from ... import ... kulcsaszavakkal a függvényt direktbe importájuk:

from time import localtime, strftime
t = localtime()
current_time = strftime("%Y/%m/%d %H:%M:%S", t)
print(current_time)
2024/05/02 09:51:17

Tipp

A help függvény segítségével kaphatunk információt egy függvényről, például:

help(strftime)
help(strftime)
Help on built-in function strftime in module time:

strftime(...)
    strftime(format[, tuple]) -> string
    
    Convert a time tuple to a string according to a format specification.
    See the library reference manual for formatting codes. When the time tuple
    is not present, current time as returned by localtime() is used.
    
    Commonly used format codes:
    
    %Y  Year with century as a decimal number.
    %m  Month as a decimal number [01,12].
    %d  Day of the month as a decimal number [01,31].
    %H  Hour (24-hour clock) as a decimal number [00,23].
    %M  Minute as a decimal number [00,59].
    %S  Second as a decimal number [00,61].
    %z  Time zone offset from UTC.
    %a  Locale's abbreviated weekday name.
    %A  Locale's full weekday name.
    %b  Locale's abbreviated month name.
    %B  Locale's full month name.
    %c  Locale's appropriate date and time representation.
    %I  Hour (12-hour clock) as a decimal number [01,12].
    %p  Locale's equivalent of either AM or PM.
    
    Other codes may be available on your platform.  See documentation for
    the C library strftime function.

És hogy itt áljon egy példa, hogy mindenre van egy könyvtár Pythonban: jelenítsük meg 1848 márciusának naptárját alfanumerikusan:

import calendar
print(calendar.month(1848, 3))
     March 1848
Mo Tu We Th Fr Sa Su
       1  2  3  4  5
 6  7  8  9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31

Feladat

  • Importáljuk a __hello__ könyvtárat! Mit kapunk?

  • Importáljuk a this könyvtárat! Mit kapunk? Ez egy easter egg.

Az elágazás: ifelse és elif#

Az elágazás lehetővé teszi a kód sorról-sorra való végrehajtásának megtörését. Amennyiben az elágazás feltétele igaz, az elágazás blokkja végrehajtódik, különben az elágazás törzsében lévő kód nem kerül végrehajtásra. Íme egy példa:

a = 1
if a >= 1:
  print("true")
print("end")
true
end

Feladat

Elemezzük a fenti kódot és definiáljuk az elágazás szintaktikáját! Azonosítsuk a következőeket:

  • az elagazás feltétele

  • az elágazás blokkja

Az elágazás feltételének valamilyen logikai értékre, tehát True vagy Falsera kell kiírtékelődnie. A fenti példában például láthatjuk, hogy az összehasonlítás igazat, vagy hamisat ad:

print('Feltétel értéke: ', a >= 1)
print('Feltétel típusa: ', type(a >= 1))
Feltétel értéke:  True
Feltétel típusa:  <class 'bool'>

Megjegyezzük, hogy az automatikus típuskonverzió révén más típusok is szerepelhetnek a feltételben. Ekkor létezni kell egy a típusról logikai értékre történő alapértelmezett (automatikus) konverziónak. Így például számok esetén, a 0 érték False, míg a nem nulla érték True logikai értékké konvertálódik:

print('0 -> bool konverzió:', bool(0))
if not 0:
  print('0: A feltétel: False')
    
print('100 -> bool konverzió:', bool(100))
if 100:
  print('100: A feltétel: True')
0 -> bool konverzió: False
0: A feltétel: False
100 -> bool konverzió: True
100: A feltétel: True

A fenti kódrészletben a bool függvény segítségével direkt típuskonverziót használtunk, hogy megmutassuk a típuskonverzió értékét. Ezután az if feltételekben a bool függvényt már nem használtuk, így automatikus típuskonverzió révén kerül a feltétel kiértékelésre.

Az else ág lehetőséget ad arra, hogy olyan kód blokkot adjunk meg, amely abban az esetben kerül végrehajtásra, ha a feltétel nem teljesül:

a = 0
if a >= 1:
  print("if blokk")
else: 
  print("else ág")
else ág

Feladat

Ismételjük át a logikai műveletkeről tanultakat! Mit ír ki az alábbi kód?

a, b = 1, 2
if not (a == 2 or b == 2):
  print('if blokk')
else:
  print('else blokk')
    

Lehetőségünk van több feltételt megadni az elif segítségével. A következő kód például megadja, hogy egy bizonyó órához, milyen étkezés tartozik:

hour = 10
if 0 <= hour < 10:
  print("reggeli")
elif 10 <= hour < 17:
  print("ebéd")
elif 17 <= hour < 24:
  print("vacsora")
else: 
  print("nem létező idő a napon belül!")
ebéd

Feladat

Mit csinál az alábbi kód? Mi a probléma a kóddal? Teszteljük!

v = 0
if v < 0:
   abs_v = -v
elif v > 0:
   abs_v = v
    
print(abs_v)

Az if szerkezetet egyszerű esetekben (amikor egy sorba lehetne írni a blokkot) kifejthetjük egy sorban is:

a = 1
print("true") if a == 1 else print("false")
true

A fenti kódrészletben a print("true") akkor kerül végrehajtásra, ha az a == 1 feltétel igaz, máskülönben az else utáni print("false") kód kerül végrehajtásra.

Az egysoros if kifejezést használhatjuk feltételes változó értékadásra, ahogy az alábbi példában is:

a = 0
b = 1 if a == 1 else 2
print(b)
2

Vegyük észre, hogy a b = 1 if a == 1 else 2 kifejezésben, nem a b = 1 a kódrészlet, amely az igaz esetén kiértékelődik, hanem csupán az 1. Ez azért lehetséges, mert az egysoros if előrébb van a műveleti sorrendben mint az értékadás. Hogy érthető legyen, a fenti kód zárójelezve:

b = (1 if a == 1 else 2)

Végezetül, az if blokkja nem definiál hatókört a változónak. Az alábbi kódban az if blokkban létrehozott változó elérhető a globális névtérben is:

if True:
    test = "Ez a változó az if blokkban lett létrehozva"
print(test)
Ez a változó az if blokkban lett létrehozva

A None típus#

Egy változó felvehet None típust, amit úgy értelmezhetünk, hogy a változónak nincs értéle:

var = None

A None értéket ellenőrízhetjök:

if var == None: 
  print("It is None!")  
It is None!

Alpértelmezetten hamis az értéke egy if feltételben:

if var:
   print("Condition is true!")
else:
  print("Condition is false!")
Condition is false!

De vigyázzunk, maga az érték nem False vagy True, így a következő kifejezés nem működik:

if var == False:
   print("Condition is true!")
else:
  print("Condition is false!")
Condition is false!
if var == True:
   print("Condition is true!")
else:
  print("Condition is false!")
Condition is false!

A következő példa mutatja, mi a legjobb módja ellenőrizni egy változónak, hogy None az értéke:

if var is None:
   print("Condition is true!")
else:
  print("Condition is false!")
Condition is true!
if var is not None:
   print("Condition is true!")
else:
  print("Condition is false!")
Condition is false!

Függvény változók#

Nézzük meg az alábbi kódot:

def hello(name):
    print('Hello, Mr. ', name, '!')

hello('Smith')
Hello, Mr.  Smith !

Minden ismert a fenti kód részletben: létrehozunk egy függvényt, melynek bemeneti értéke egy name változó, melyet aztán kiír a függvény. Végül, az utolsó sorban kiírjuk a függvényt. Ezután nézzük meg az alábbi kódot:

def hello(name):
    print('Hello, Mr. ', name, '!')

hello2 = hello
hello2('Smith')

print(type(hello2))
Hello, Mr.  Smith !
<class 'function'>

Vegyük észre hogy a hello literál egy függvény név, melyet értékül adunk a hello2 változónak. Ezután a hello2-t használjuk, hogy meghívjuk az eredeti függvényt. Tehát a hello2 változó mögött a hello függvény található. Ha megnézzük mi a hello2 változó típusát az utolsó sorban, akkor azt látjuk, hogy az egy függvény (function). Ezeket a változókat hívjuk függvény változóknak.

Függvény változók segítségével bizonyos döntési pontokat a kódban szabadon rendezhetünk. Például lásd az alábbi kódot:

def hello_mr(name):
    print('Hello, Mr. ', name, '!')

def hello_mrs(name):
    print('Hello, Mrs. ', name, '!')

first_name = 'Jane'

# Döntési point
if first_name == 'Joe':
    hello = hello_mr
else:
    hello = hello_mrs

hello('Smith')
Hello, Mrs.  Smith !

A fenti kódban, a személy megszólítása Mr. vagy Mrs. annak függvényében, hogy a személy keresztneve Joe vagy Jane. A kód először definiálja a két kiírási módot, majd a hello változó felveszi valamelyik függvény változatát a kersztnév alapján. Ezután a hello függvény meghívásakor a megfelelő megnevezés fog meghívódni.

A fenti példa egyszerűsége miatt erőltetettnek tűnhet, de gyakran hasznos. Egy gyakori példa, a kódban található ellenörző kódok kiírása (loggolás). A gyakorlatban bizonyos kiírásokat csak fejlesztéskor szeretnénk látni, hogy a fejlesztő ellenörizni tudja a kód belső működését, ezt hívjuk debug módnak. Amikor a programot kiadják, tehát elérhetővé teszik a végfelhasználó számára (release mód), akkor ezeket a kiírásokat nem szeretnénk a felhasználó számára láthatóvá tenni. Egy lehetséges megoldás, hogy a kódbázison végigmenve kitörölni azokat a kódrészleteket, melyek ilyen kiírást végeznek. Ez manuálisan intenzív, illetve a folyamatos fejlsztéskor, ezekre a kódrészletkre szükségünk lehet. Nézzük meg az alábbi példát, hogyan oldhatjuk meg ezt egyszerűen függvényváltozó segítségével:

def log_debug(str):
    print(str)

def log_release(str):
    pass

mode = 'release'

log = log_debug
if mode == 'release':
    log = log_release

# Komplikált számítás loggolással
import math
lat = 47.4809437284
lon = 19.0565297521
alt = 160

rad_lat = lat * (math.pi / 180.0)
log(rad_lat)
rad_lon = lon * (math.pi / 180.0)
log(rad_lon)
a = 6378137.0
finv = 298.257223563      
f = 1 / finv
e2 = 1 - (1 - f) * (1 - f)
log(e2)
v = a / math.sqrt(1 - e2 * math.sin(rad_lat) * math.sin(rad_lat))
log(v)

x = (v + alt) * math.cos(rad_lat) * math.cos(rad_lon)
y = (v + alt) * math.cos(rad_lat) * math.sin(rad_lon)
z = (v * (1 - e2) + alt) * math.sin(rad_lat)

print(x, y, z)
4081869.0840466293 1410006.5497940828 4678184.052527026

A fenti kód nem csak az eredményt írja ki az utolsó sorban, mivel release módban vagyunk. Ha a mode változót megváltoztatjuk debug-ra a log hívás ki fogja írni a köztes számítások értékét, melyet használhatunk. Tehát látható, hogy a két kiírási mód között egyszerűen egy változó értékének a megváltoztásával elegánsan tudunk váltani.

Másik gyakorlatban előforduló példa, amikor egy függvény hívásakor egy másik függvény belső állapotának értékeire is szükség van. Ekkor a függvény változó - mint bármely más változó - szerepelhet a függvény bemeneti paramétereként. Nézzük meg az alábbi példát:

def integral(fn, a, b):
    return 0.5 * (fn(a) + fn(b)) * (b - a)

def poly(x):
    return x**2/3 + x**(1/2)

val = integral(poly, 0, 0.12)
print(val)

import math
print(integral(math.sin, 0, 0.12))
0.021072609690826527
0.007182732437335162

A fenti példában az integral függvény egy bemeneti fn függvény integrálját számítja ki a trapéz szabály szerint. Látható, hogy a függvény szempontjából az hogy mi a függvény maga, nem számít, csak hogy valamilyen valós bemeneti értékre adjon egy valós kimenetet. Az integrálandó függvényt a poly függvényben definiáljuk, melyet ezután át tudunk adni az integral függvénynek, mely megadja az integrál közelítő értéket. Hasonló módon, az integral függvényt a math könyvtárban lévő szinusz függvényre is tudjuk alkalmazni.

Függvény változókat alkalmazó megoldásokkal gyakran találkozhatunk a Python különböző könyvtáraiban.

Névtelen vagy lambda függvények#

Nézzük meg az alábbi függvényt:

def fn(x):
    return x**2/3 + x**(1/2)

print(fn(4))
7.333333333333333

Névtelen vagy lambda függvények, olyan függvények melyek törzsét helyben fejtük ki. A lambda függvények egyszerűsítenek a kódon, olvashatóbbá teszik azt. Nagy hátrányuk, hogy csak egy sornyi kód adható meg velük.

A fenti kód lambda függvénnyel kifejezve:

fn = lambda x: x**2/3 + x**(1/2)

hello(fn(4))
print(type(hello))
Hello, Mrs.  7.333333333333333 !
<class 'function'>

A lambda függvénynek, mint minden függvénynek van bemeneti és kimeneti paramétere. A bemeneti paraméter a lambda kulcszó után található lista, melyet a : zár le. A lambda függvény értékadásra kerülhet egy változónak, ahogy az a fenti példában is látható. A változó függvény típusú változó lesz. A változó visszatérési értéke a : utáni kód értéke.

Egy példa lambda függvényre két bemeneti változóval, amely egy téglalap két oldala alapján annak területét számítja ki:

A = lambda a, b: a*b
print(A(10, 2))
20

Nézzük meg, hogy lambda függvénnyel hogyan egyszerűsíthető a korábbi integrálos példánk:

def integral(fn, a, b):
    return 0.5 * (fn(a) + fn(b)) * (b - a)

val = integral(lambda x: x**2/3 + x**(1/2), 0, 0.12)
print(val)
0.021072609690826527

Látható, hogy nincs szükség külön def kulcsszóval a függvény definiálására, ezt helyben, a függvény hívásakor meg tudjuk adni.

Feladat

Tudunk-e olyan problémát találni, ahol függvény változók, vagy lambda függvények hasznosak lehetnek.