Bonyolultabb programok sok egymás után következő utasítás végrehajtásából állnak. Azt, hogy melyik utasítás mikor kerül végrehajtásra, a vezérlőutasítások határozzák meg. Minden program nyelvben két alapvető vezérlő utasítás mindenképpen megtalálható, a feltételválasztó és a ciklusszervező.
In [1]:
if 2+2==4:
print('A matematika még mindig működik')
Figyelem, a második sort beljebb húztuk! A behúzás a Python jelölése az utasítások csoportosítására!
A behúzás mértékére általánosan bevett szokás, hogy a szintaktikailag alacsonyabb szintű programrészek négy szóközzel beljebb vannak tolva. Ha tehát kétif
utasítást ágyazunk egymásba, akkor a második által teljesítendő utasítások 8 szóközzel lesznek beljebb húzva. Ez a behúzás a jupyter
notebook kódcelláiban automatikusan is megtörténik. A :
utáni ENTER leütése után a következő sor 4 szóközzel kerül beljebb, további ENTER leütése hatására a hierarchia azonos szintjén kezdjük az új sort. Ha feljebb szeretnénk lépni, akkor a behúzást ki kell törölni. A C, C++, Java nyelvekben kapcsos zárójeleket {} használnak az egybefüggő kódrészletek elkülönítésére, kb. így:
if (i==10) {
print i
}
A FORTRAN nyelvben az END szócska kiírása jelzi a kódrészlet végét. A pythonban a kettőspont (":"), majd szóközökkel beljebb írt sorok szolgálnak erre. Ha más programokból másolunk át részleteket, figyeljünk arra, hogy a behúzás helyett nem TAB-ot használ-e. (A TAB néhol megengedett, de kerülendő. A modern python kódolási stílusirányzat minden behúzást, ahogy azt fent is említettük, 4 szóköznek javasol.)
In [2]:
today='Monday';
time='12:00';
if today=='Monday':
if time=='12:00':
print('Nyomassuk a pythont!')
Bonyolultabb kritériumszerkezetet az else
és elif
parancsok segítségével konstruálhatunk:
In [3]:
x = 1
if x < 0:
x = 0
print('Negatív, lecseréltem nullára')
elif x == 0:
print('Nulla')
elif x == 1:
print('Egy')
else:
print('Egynél több.')
Hiányozhat, de lehet egy vagy akár egynél több elif
rész, az else
rész szintén elmaradhat. Az elif
kulcsszó – amely az ‘else if’ rövidítése – hasznos a felesleges behúzások elkerülésre. Egy if
...elif
... elif
... sor helyettesíti a más nyelvekben található switch
és case
utasításokat.
for
utasításA számítógépek legfontosabb tulajdonságai közt szerepel az, hogy nagyon gyorsak és "fáradhatatlanok". Olyan feladatok megoldásában a leghatékonyabbak, amikor a feladatot kevés munkával meg lehet fogalmazni ("az alkotó pihen") de végrehajtása nagyon sok ismétlést, iterációt igényel ("a gép forog"). Az iteráció (angol: iterate) azt jelenti, hogy például egy lista elemein egyesével végigmegy a program, és műveleteket végez el rajtuk. A Python-ban az egyik erre használható utasítás a for
parancs (magyarul kb. a ...-ra, azaz pl. a hét minden napjára, a lista minden elemére):
In [4]:
days_of_the_week = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]
In [5]:
for day in days_of_the_week:
print(day)
Ez a kódrészlet a days_of_the_week
listán megy végig, és a meglátogatott elemet hozzárendeli a day
változóhoz, amit ciklusváltozónak is neveznek. Ezek után mindent végrehajt, amit a beljebb tabulált (angolul: indented) parancsblokkban írtunk (most csak egy print
utasítás), amihez felhasználhatja a ciklusváltozót is. Miután vége a beljebb tabulált régiónak, kilép a ciklusból.
Annak semmi jelentősége nincs, hogy a példában a day nevet adtuk az iterációban szereplő ciklusváltozónak. A program semmit se tud az emberi időszámításról, például, hogy a hétben napok vannak és nem kiscicák:
In [6]:
for macska in days_of_the_week:
print(macska)
A ciklus utasításblokkja állhat több utasításból is:
In [7]:
for day in days_of_the_week:
statement = "Today is " + day
print(statement)
A range()
parancs remekül használható, ha a for
ciklusban adott számú műveletet szeretnénk elvégezni:
In [8]:
for i in range(20):
print(i," szer ",i ,"az pontosan ",i*i)
Akkor válik mindez még érdekesebbé, ha az eddig tanult iterációt és feltételvizsgálatot kombináljuk:
In [9]:
for day in days_of_the_week:
statement = "Today is " + day
print(statement)
if day == "Sunday":
print (" Sleep in")
elif day == "Saturday":
print (" Do chores")
else:
print (" Go to work")
Figyeljük meg, a fenti példában hogy ágyazódik egymás alá a for
és az if
!
A Fibonacci-sorozat első két eleme 0 és 1, majd a következő elemet mindig az előző kettő összegéből számoljuk ki: 0,1,1,2,3,5,8,13,21,34,55,89,...
Ha nagyobb $n$ értékekre is ki akarjuk számolni a sorozatot, ez kiváló feladat lehet egy fáradhatatlan és gyors számítógépnek!
In [10]:
n = 10 # ennyi elemet szeretnénk meghatározni
sequence = [0,1] # az első két elem
for i in range(2,n): # számok 2-től n-ig, Figyelni kell, hogy n ne legyen kisebb mint 2!!
sequence.append(sequence[i-1]+sequence[i-2])
print (sequence)
Nézzük végig lépésről lépésre! Először $n$ értékét, azaz a kiszámolandó sorozat hosszát állítjuk be 10-re. A sorozatot majdan tároló listát sequence
-nek neveztük el, és inicializáltuk az első két értékkel. A "kézi munka" után következhet a gép automatikus munkája, az iteráció.
Az iterációt 2-vel kezdjük (ez ugye a 0-s indexelés miatt a 3. elem lesz, hisz az első kettőt már mi megadtuk) és $n$-ig, a megadott listaméretig számolunk.
A ciklus törzsében az addig kiszámolt lista végére hozzátűzzük (append
parancsról kicsit bővebben itt) az előző két tag összegét. A ciklus vége után kiíratjuk az eredményt.
Számos programozási nyelvben szokás a felhasználó által megalkotott bonyolultabb utasításokat függvényekbe szervezni. Jól megírt függvények használata általában rövidebbé és jobban átláthatóvá teszi a programokat.
Ha más hosszúságú sorozatot szeretnénk, átmásolhatjuk a fenti kódot egy új cellába, és átírhatjuk az n=10
-et pl. n=100
-ra. Van azonban egy hatékonyabb módszer, új függvény-ként definiálhatjuk a def utasítás segítségével:
In [11]:
def fibonacci(sequence_length):
"A Fibonacci sorozat elso *sequence_length* darab eleme" # ez csak a 'help'-hez kell
sequence = [0,1]
if 0 < sequence_length < 3:
return sequence[:sequence_length]
for i in range(2,sequence_length):
sequence.append(sequence[i-1]+sequence[i-2])
return sequence
Most már meghívhatjuk a fibonacci()
függvényt különböző hosszakra:
In [12]:
fibonacci(5)
Out[12]:
Elemezzük a fenti kódot! A már megszokott módon a kettőspont és behúzás határozza meg a függvénydefinícióhoz tartozó kódblokkot. A 2. sorban idézőjelek közt szerepel a "docstring", ami a függvény működését magyarázza el röviden, és később a help paranccsal hívható elő:
In [13]:
help(fibonacci)
A notebookos környezetben a docstring a ? segítségével is elérhető:
In [14]:
?fibonacci
A docstring
-et jupyter
környezetben úgy is megtekinthetjük, ha egyes függvények hasában, azaz a zárójelek között SHIFT+TAB-ot nyomunk. Próbáld ki ezt az alábbi cellán (annélkül hogy lefuttatnád azt)!
In [ ]:
fibonacci()
A függvény kimenetelét a return
kulcsszó határozza meg. Ha a fügvény definiálása során nem használtunk return
utasítást akkor a függvény None (semmi) értéket ad vissza. Ha egy függvény lefutott, és nem hajtott végre return
utasítást, akkor is None értékkel tér vissza. A fibonacci fügvény például egy listát ad vissza:
In [15]:
x=fibonacci(10)
x
Out[15]:
Ez a függvény viszont nem tér vissza sehol:
In [16]:
def ures_fuggveny(x):
print('Én egy ures fuggvény vagyok,\nannak ellenére hogy beszélek,\nnem térek vissza változóval!!')
y=x-2;
Így a z válltozóban nem tárolódik semmilyen érték!
In [17]:
z=ures_fuggveny(3)
z
In [18]:
print(z)
Egy függvénynek lehet több bemeneti változója is:
In [19]:
def osszead(a,b):
return a+b
Az is előfordulhat, hogy több értéket szeretnénk visszakapni egy függvényből. Ezt például az alábbiak alapján tehetjük meg:
In [20]:
def plusminus(a,b):
return a+b,a-b
In [21]:
p,m=plusminus(2,3)
print (p)
print (m)
Előfordulhat, hogy egy függvénynek sok bemenő paramétere van, vagy hogy egy függvény bemenő paramétereit egy másik függvény eleve egy listába rendezi. Egy tipikus ilyen példa, amint azt későb látni fogjuk, a függvényillesztés esete. Ilyenkor a paramétereket tartalmazó lista "kicsomagolásával", amit a *-jellel tehetünk meg, kompaktabb kódot érhetünk el. Vegyünk például egy olyan esetet amikor egy adatsorra egy ötödfokú polinomot illesztünk.
Definiáljunk egy függvényt ami egy "futó" $x$ változó és az ötödfokó polinomnak megfelelően hat darab "paraméter" $a_i$ változó segítségével kiértékeli a fenti polinomot:
In [22]:
# ez lesz az illesztendő függvény
def poly5(x,a0,a1,a2,a3,a4,a5):
return a0+a1*x+a2*x**2+a3*x**3+a4*x**4+a5*x**5
Az illesztés során hat darab illesztési paramétert határozunk meg: $a_0,a_1,a_2,a_3,a_4,a_5$ ám ezeket az illesztő program egy listába rendezve adja a kezünkbe:
In [23]:
# ezek az illesztés során meghatározott paraméterek
# az alábbi sorrendnek megfelelően
# params=[a0,a1,a2,a3,a4,a5]
params=[ 2.27171539, -1.1368942 , 0.65380304, -0.25005187, -0.1751268 , -0.48828309];
Ha ki szeretnénk értékelni az illesztett polinomot az $x=0.3$ helyen, akkor azt megtehetjük az alábbi módon:
In [24]:
poly5(0.3,params[0],params[1],params[2],params[3],params[4],params[5])
Out[24]:
vagy az ennél sokkal kompaktabb módszerrel:
In [25]:
poly5(0.3,*params)
Out[25]:
Ez a konstrukció lehetővé teszi, hogy a függvény definiálása során felkészísük a függvényt arra, hogy a bemenő paraméterek száma ne legyen rögzített. Ha a függvény deklaráció során egy paraméter elé *-t teszünk akkor az a paraméter tetszőleges hosszúságú lehet! Vizsgáljuk meg az alábbi példát:
In [26]:
def adok_mit_kapok(*argv): #Így, a *-al, készítünk fel egy függvényt
#válltozó számú paraméter fogadására
print("Nekem ",len(argv),"db bemenő paraméterem jött")
for arg in argv:
print ("Ez egy paraméter :", arg)
return argv[-1] #a bemenő paraméterek elemeire mint
#szokványos lista elemekre hivatkozunk
A fenti blokkban definiált függvény tetszőleges számú bemenő paramétert elfogad! A futás során közli, hogy hány paraméter érkezett, kiírja azokat, illetve a paraméterlista utolsó tagját mint a függvény vissza térési értékét állítja be.
In [27]:
adok_mit_kapok('Gáspár','Menyhért','Boldizsár')
Out[27]:
Természetesen a tetszőleges paraméter helyett egy "kicsomagolt" tetszőleges hosszúságú listát is használhatunk!
In [28]:
adok_mit_kapok(*params) #természetesen itt is működik a kicsomagolás..
Out[28]:
Azon kívül, hogy a szótárak már önmagukban is igen hasznos adatstruktúrák, később látni fogjuk, hogy sokszor függvények bizonyos paramétereit is szokás szótárakba szedni. Az ilyen paramétereket szokás kulcsszavas változóknak vagy kulcsszavas argumentumnak (angol nyelven keyword argument) hívni. Ezek használata olvashatóbbá és emberi szemmel is jóval értelmezhetőbbé teszi a programot, ezenfelül további flexibilitást is ad a programozó kezébe. Nézzük ezt meg az alábbi példán:
In [29]:
#Így adunk meg alapértelmezett értékeket
def students(ido, allapot='lelkesen figyelik a tanárt', tevekenyseg='kísérletezés', ora='fizika'):
print("Ezek a diákok "+ora+"órán "+tevekenyseg+" közben mindig "+allapot+"!");
print("Még akkor is, ha épp ",ido,"-t mutat az óra!");
A függvény első paraméterét meg kell adnunk, ha nem adunk többet, akkor az alapértelmezett értékek ugranak be:
In [30]:
students('17:00')
Ha egy kulcsszavas argumentumot kap a függvény, akkor azt értelemszerűen használja:
In [31]:
students('8:00',allapot='unott képet vágnak')
A kulcsszavas argumentumok sorrendjére nem kell figyelni:
In [32]:
students('8:00',ora='ógörög',allapot='pánikolva izzadnak',tevekenyseg='TÉMAZÁRÓ')
Ha a deklaráció során nem használt kulcsszót adunk meg, akkor hibát kapunk:
In [33]:
students('17:00',tanar='Mici néni')
Hasonlóan problémába ütközünk, ha egy kulcsszót kétszer is alkalmazunk:
In [34]:
students('8:00',ora='ógörög',ora='kémia')
Kulcsszavas argumentumok szótárának kicsomagolása **-jel segítségével történik:
In [35]:
diak_hozzaallas={'ora':'ének','allapot':'nyüszítenek'};
students(12,**diak_hozzaallas)
Amint a sima paramétereknél is, itt is előfordulhat az, hogy tetszőleges hosszúságú dict-et szeretnénk feldolgozni. Erre példa, ha egy olyan függvényt írunk, amely esetleg több más fügvényt hív, melyeknek tovább akarjuk adni a bejövő paraméterek egy részét.
Az alábbi függvény egy tetszőleges szótárat vár a bemenetre, megnézi hogy milyen hosszú, és ha a szótárban van 'hamburger' címkéjű kulcsszó, akkor annak az értékét adja vissza:
In [36]:
def kulcsot_adok_amit_kapok(**szotar):
print('A szotár hossza:',len(szotar))
for kulcs in list(szotar.keys()):
if kulcs=='hamburger':
print('Van hamburger!')
return szotar[kulcs]
In [37]:
kulcsot_adok_amit_kapok(makaróni=1,torta='finom') #itt már nem jelent hibát ha előre meg nemhatározott kulcsszavaink vannak!
In [38]:
kaja={'makaróni':1,'torta':'finom','hamburger':137,'saláta':'nincs'}
kulcsot_adok_amit_kapok(**kaja)
Out[38]:
Amint azt a fentiekben láttuk, függvényeknek többféleképpen is adhatunk paramétereket,
Sokszor előfordul, hogy egy függvénynek mind a négy fajta bemenete is lehet. Illetve hogy néhány változónak alapértelmezett értéket is adunk. Függvények definiálásánál használjuk a fenti sorrendet!
Például:
In [39]:
def bonyolult_fuggveny(valtozo1,valtozo2,valtozo3='ELZETT',*args,**kwargs):
if ((len(args)==0 and len(kwargs)==0)):
return valtozo3+str(valtozo2)+str(valtozo1)
elif (len(args)!=0 and len(kwargs)==0):
return 'Van valami az args-ban!'
elif (len(args)==0 and len(kwargs)!=0):
return 'Van valami az kwargs-ban!'
else:
return 'Mindenféle változónk van:'+str(valtozo1)+str(valtozo2)+valtozo3+str(args)+str(kwargs)
A fenti függvény első két változója "sima" változó, a harmadik egy kulcsszavas változó az alapértelmezett 'ELZETT' értékkel, és ezen kívül megengedünk még egyéb tetszőleges hosszú "sima" változók listáját (args), illetve tetszőleges hosszú kulcsszavas változók listáját (kwargs). Nézzük meg hogy a notebook során korábban definiált változókat felhasználva milyen viselkedést mutat a fent deklarált függvény:
In [40]:
bonyolult_fuggveny(1,2)
Out[40]:
In [41]:
bonyolult_fuggveny(1,2,valtozo3='MULTLOCK')
Out[41]:
In [42]:
bonyolult_fuggveny(1,2,*days_of_the_week)
Out[42]:
In [43]:
bonyolult_fuggveny(1,2,**kaja)
Out[43]:
In [44]:
bonyolult_fuggveny(1,2,*days_of_the_week,**kaja)
Out[44]:
Egy függvénynek nemcsak változókat, hanem más függvényeket is megadhatunk bemenetként. Például gondolhatunk egy olyan függvényre, ami egy matematikai függvényt ábrázol!
Ilyen esetekben, amikor egy függvény a bemenetére másik függvényt vár, sokszor előfordul, hogy hosszadalmas külön definiálni a bemeneti függvényt. Ekkor tömörebb használni az úgynevezett lambda
-formákat.
Nézzünk erre egy példát! Definiáljunk egy függvényt, ami egy másik függvényt értékel ki egy adott helyen, kiírja a kiértékelési helyet, és visszatér a kiértékelt függvényértékkel:
In [45]:
def funfun(g,x):
print('Ez volt az x változó: ',x)
return g(x)
In [46]:
def fx(x):
return x**2-1/x;
funfun(fx,0.1)
Out[46]:
Ekkor egy úgynevezett lambda
-forma segítségével alkalmazhatjuk az ennél kicsivel kompaktabb kifejezést (megúszva a függvénydeklarációt):
In [47]:
funfun(lambda x:x**2-1/x,0.1)
Out[47]:
A fenti kifejezésben a
lambda x:x**2-1/x
kódrészlet egy "anonim" függvényt definiál, amely az $x$ változóhoz hozzárendeli az $x^2-1/x$-et.
A lambda
-kifejezésekkel lehet többváltozós függvényeket is definiálni. Például az alábbi
lambda x,y:x**2-1/y
kifejezés a
def fxy(x,y): return x**2-1/y
függvénnyel ekvivalens.