Elérkeztünk oda, ami anno 3 éve nem sikerült, és végre új részt prezentálhatok a Python tutorialomból.
Az előző részekben a típusoktól és változóktól eljutottunk egészen odáig hogy képesek voltunk a felhasználótól adatot beolvasni a konzolon keresztül. Most következik az a rész, amikor végre a számítógép is csinálni fog valamit, úgyhogy jöjjenek az elágazások és ciklusok, avagy a vezérlési szerkezetek (control flow).
Elágazások
Azon kívül hogy a számítógép nagyon jól össze tudja adni a számokat, feltételeket is tud vizsgálni, és a vizsgálat eredményének függvényében a program futása különböző utakon haladhat tovább. Ezt hívjuk elágazásnak.
Elágazás létrehozásához az if
(ha) utasítást kell használnunk. Nézzük hogy hogyan működik:
number = int(input('Írj be egy számot: '))
if number > 100:
print('A szám nagyobb mint 100')
A 2. sortól érdekes a történet. Az if
utasítás mellett egy olyan kifejezés következik, aminek van logikai értéke. A number > 100
kifejezés, ha a number
értéke nagyobb mint 100, akkor True
, amúgy False
kiértékelési értéket kap. A sort mindig a :
zárja, erre figyeljünk.
A 3. sorban valami tök új dolgot látunk, méghozzá hogy ez a sor kicsit beljebb kezdődik mint a többi. A Pythonban ilyen bekezdésekkel (indent, indentation) tudunk kód blokkokat (törzseket) létrehozni. Egyes utasítások, mint az if
is, megkövetelik hogy új blokkot nyissunk utánuk. Ilyen utasítás nélkül, csak úgy poénból nem tologathatjuk kifelé-befelé a forráskódot kedvünk szerint, mert IndentationError
hibaüzenetet kapunk.
Általában elmondható, hogy amelyik utasítás végére
:
-t kell írni, az alatt új blokkot kell nyitni.
Futtassuk a kódot és írjunk be egy 100-nál nagyobb számot. Válaszul az A szám nagyobb mint 100 üzenetet kapjuk.
Futtassuk még egyszer a programot, de most írjunk egy 100-nál kisebb értéket. A konzolban semmi se jelenik meg. Láthatóvá vált, hogy az if
blokkja akkor kerül futtatásra, ha az if
melletti kifejezés True
értékű. Lehetőségünk van szerencsére rögtön, ugyan ebben a szerkezetben kezelni a False
értéket is, ehhez az else
(különben) utasítást kell használni.
number = int(input('Írj be egy számot: '))
if number > 100:
print('A szám nagyobb mint 100')
else:
print('A szám kisebb mint 100')
Az else
és az if
egy párt alkotnak, ezért azonos bekezdést kell alkalmazni előttük (azonos bekezdési szintent (indentation level) kell lenniük).
Futtassuk így a programot. Így akár 100-nál nagyobb, akár kisebb értéket írunk be, mindig kapunk valamilyen eredményt, viszont ha pont 100-at írunk be, akkor azt kapjuk hogy A szám kisebb mint 100, ami egyáltalán nem igaz, ezért jó lenne ezt az esetet is kezelni.
Az if
és else
kombinálató egy 3. utasítással, ez az elif
(különben ha).
number = int(input('Írj be egy számot: '))
if number > 100:
print('A szám nagyobb mint 100')
elif number == 100:
print('A szám pont 100')
else:
print('A szám kisebb mint 100')
Ez a program végre tényleg jól működik. Vegyük végig soronként hogy mi történik néhány konkrét érték esetén:
- Ha 99-et írunk be, akkor az első feltétel (
number > 100
) hamis lesz, így ugrunk a következőre (number == 100
), de ez is hamis. Nincs többelif
ág, ezért mindenképp azelse
-ben kötünk ki. - Ha 100-at írunk be, akkor az első feltétel (
number > 100
) hamis lesz, így ugrunk a következőre (number == 100
) ami igaz lesz, így bekerülünk ebbe az ágba. - Ha 101-et írunk be, akkor az első feltétel (
numer > 100
) rögtön igaz lesz, így nem is vizsgálódunk tovább.
Ebben a három pontban igyekeztem nyomatékosítani, de leírom külön is, hogy ha a program futása bekerült valamelyik elágazás blokkjába, akkor a blokk végével a többi feltétel már nem értékelődik ki, hanem véget ér a teljes szerkezet vizsgálata. A következő kód jól mutatja a feltételek sorrendjének fontosságát:
number = int(input('Írj be egy számot: '))
if number > 100:
print('A szám nagyobb mint 100')
elif number > 200:
print('A szám nagyobb mint 200')
elif number > 300:
print('A szám nagyobb mint 300')
Ha beírjuk azt hogy 301, akkor azt kapjuk válaszul hogy A szám nagyobb mint 100, pedig az első elif
ág is igaz lett volna… meg a második is… viszont az if
ág előbb volt igaz. Mindezekből következik, hogy ha feltételeket írunk akkor legelőször érdemes a legszűkebb esetet írni, és ahogy haladunk lefelé, úgy lazítunk a megszorításokon.
Az elágazási szerkezetet tehát mindig if
-el kell kezdeni, tetszőlegs számú elif
lehet benne, és igény szerint lehet benne else
, ami mindig a legutolsó ága a szerkezetnek.
Kód blokkok
Azt már tudjuk, hogy kód blokkot bekezdéssel hozunk létre. A bekezdéseinket mindig 4 db szóközzel toljuk beljebb (ahogy a fenti példáimban is tettem). A blokk mindig addig tart ameddig azonos bekezdésű sorok követik egymást. Az üres sorok nem zárják le a blokkot, és nem kell beléjük felesleges szóközöket írni.
Ezen a szellős kódon ez jól látszódik:
number = int(input('Írj be egy számot: '))
if number % 2 == 0:
print('A szám páros')
print('A szám fele:', number / 2)
else:
print('A szám páratlan')
print('A szám kétszerese:', number * 2)
print('Ezt gépelted be:', number)
Akár kevesebb vagy több szóközt, sőt, szóköz helyett tabulátort is használhatunk, de a programozók nagy többsége, és a Python nyelv fejlesztői is a 4 db szóköz használatát ajánlják.
Ciklusok
A programozási nyelvekben a ciklus (loop) azt jelenti, hogy a számítógép valamilyen feltétel függvényében egy blokknyi kódot folyamatosan újra és újra lefuttat.
A Python nyelvben kétféle ciklus létezik: a while
és a for
.
A while ciklus
A while
(amíg) utasítás klasszikusan valósítja meg azt a definíció-szerűséget, amivel ezt a fejezetet bevezettem: addig futtatja a blokkjában szereplő kódot, amíg a neki adott kifejezés logikailag igaz értékű. Lássuk az ide vágó példát is: írjuk ki a számokat 1-10-ig.
counter = 1
while counter <= 10:
print(counter)
counter = counter + 1
Az 1. sorban deklaráltuk a counter
változót 1
értékkel.
A 2. sor az érdekes: a while
melletti feltételétől függ, hogy meddig kell a while
blokkját ismételtetni. Ezt magyarul bentmaradási feltételnek szokták hívni.
Nézzük lépésenként hogy mi történik innentől:
- A program futása a
while counter <= 10
-re került. Itt acounter
értéke1
, így a kifejezés egyenlő a1 <= 10
-el, amiTrue
-ra értékelődik ki. Így belépünk a blokkba. - Kiírjuk a
counter
értékét. - A
counter
változ értékét növeljük 1-el. Ez nagyon fontos. - Véget ért a blokk, vissza ugrunk a
while
sorára. - A
counter
értéke most már2
, így a kifejezés egyenlő a2 <= 10
-el, amiTrue
-ra értékelődik ki. Így újra belépünk a blokkba.
Ez a fenti játék egészen addig fut, amíg a counter
el nem éri a 11-et, akkor ugyanis:
- A
counter
értéke 11, így a kifejezés egyenlő11 <= 10
-el, amiFalse
-ra értékelődik ki, így nem lépünk be a blokkba. - A blokkon kívüli forráskóddal folytatódik a program.
Látható volt, hogy a while
kifejezése még ez előtt kiértékelődött mielőtt beléptünk volna újra a blokkba. Ezért nem jelent meg a 11 a konzolban. A ciklust e tulajdonsága miatt előltesztelő ciklusnak hívják.
Azt amikor egyszer lefut a ciklus blokkja iterációnak (iteration) hívják.
Írjunk egy olyan programot, ami addig olvas be és ad össze tört számokat, amíg egy üres Enter
-t nem nyom a felhasználó.
sums = 0
next_number = input('Írj be egy számot: ')
while next_number != '':
sums = sums + float(next_number)
next_number = input('Írj be egy számot: ')
print('Az összeg:', sums)
A sums
változóban tároljuk majd a részösszeget, (tehát minden iterációban ehhez a változóhoz adjuk majd a begépelt számokat). Ez után kiírjuk az Írjon be egy számot: üzenetet, és a begépelt szöveget eltároljuk a next_number
változóban, majd következik is a ciklus.
A feltétel szerint addig ismétlődik a ciklus, amíg a next_number
értéke nem üres string. Ilyet akkor kapunk, a nem írunk be semmit, csak egy üres Enter-t nyomunk. Ha rögtön ezzel kezdett a felhasználó, akkor be se lépünk a ciklusba hanem szépen elballagunk mellette. Viszont ha begépelt valamit (akármit), akkor már bent is vagyunk a blokkban.
Itt float
-á alakítjuk a next_number
értékét, és ezt hozzá adjuk a sums
változóhoz, így növeltük a részösszeget (azért használunk float
-ot, hogy tört számokkal is működjön a program). A ciklus vége a lényeg: itt szólítjuk fel újra a felhasználót, hogy gépeljen be egy számot, és az eredményt ugyan abba a next_number
változóba írjuk, ahova a cikluson kívül is tettük. A futás vissza ugrik a while
sorára, ezzel véget ért egy iteráció. Innen pedig az utoljára begépelt értéktől függően vagy belépünk újra a ciklusba, vagy kihagyjuk és megérkezünk az összeg kiírásához.
Ciklus megszakítása, új iteráció indítása
Létezik két speciális utasítás, amit csak ciklusok törzsében lehet csak használni: a continue
és a break
.
A continue
utasítás hatására azonnal új iterációt indít a ciklus. Ez például akkor lehet hasznos, ha át akarunk ugrani egy értéket.
counter = 0
while counter <= 10:
counter = counter + 1
if counter % 2 == 0:
print(counter, 'páros')
continue
print(counter, 'páratlan')
Ez egy igen béna kis példa, de a lényeg benne van: ha a counter
változó értéke 2-vel osztható, akkor kiírjuk hogy páros és indítjuk is az új iterációt. Ha az érték páratlan, akkor sosem lépünk be a feltétel törzsébe, és megjelenik a páratlan felirat.
A break
utasítás hatására azonnal kiugorhatunk a ciklusból Ez akkor jön jól, ha még az előtt be akarnánk fejezni a ciklust, hogy az természetesen (értsd: a feltételében megadott kifejezés függvényében) befejeződne. Általában végtelen ciklus esetén van erre szükség mert hát amúgy… végtelen sokáig menne a ciklus.
A végtelen ciklus
Előfordulnak olyan szituációk, amikor véletlenül vagy direkt olyan kifejezést írunk, ami örökké igaz lesz. Ilyenkor a ciklus soha sem áll le; ezt hívják végtelen ciklusnak (infinite loop). Vegyük a legelső példa kódot ebből a részből. Ha a végéről elhagynánk a counter
növelését, akkor a ciklus feltétele örökké igaz lenne, hisz az 1
mindig kisebb a 10
-nél; így a program örökké és koxon azt írná a konzolba hogy 1.
Ez elsőre tök haszontalannak tűnhet, de ha egyszerűen nem tudjuk hogy mikor kéne véget érnie a ciklusnak, akkor a legjobb egy végtelen ciklust indítani. Tipikusan ilyen eset az, amikor a felhasználótól várunk valamilyen értéket (vagy a hálózatról).
Vegyük a korábbi példát. Az igaz hogy nincs benne végtelen ciklus, de sajnos az a gond vele hogy redundáns (ismétlés van benne): az input()
kétszer, ráadásul azonos üzenettel van meghívva benne.
Úgy is megközelíthettük volna a problémát, hogy mivel nem tudjuk hogy mennyi számot gépelnek majd be, ezért egy végtelen ciklust indítunk, és minden iterációban felszólítjuk a gépelésre a felhasználót. Viszont ha a begépelt érték egyszer egy üres string lesz, akkor jól kiugrunk a ciklusból.
sums = 0
while True:
next_number = input('Írj be egy számot: ')
if next_number == '':
break
else:
sums = sums + float(next_number)
print('Az összeg:', sums)
A while True:
sor egy gyakran visszatérő “jelenség” Pythonban. Végtelen ciklusokat általában így szoktak indítani (természetesen állhatna it 1 == 1
, 1 < 2
, stb, de tartsuk magunkat a többiekhez).
A ciklus törzsének nagy része szerintem nem érdemel magyarázatot. A break
utasításnál, bár egy feltételben szerepel, de a feltétel a ciklusban van, így ide írható ez az utasítás. Az értelmezésével pedig természetesen a ciklusból, és nem a feltételből ugrunk ki (olyat nem lehet csinálni).
Ezt a mintát (pattern) használják a Pythonban a hátultesztelő ciklusok szimulálására, mert nem létezik külön szerkezet erre az esetre.
Hátultesztelő ciklusnak azt nevezzük, amikor a ciklus törzse fut le először, és csak utána döntjük el, hogy következhet-e iteráció.Azon kívül hogy mindenki a
while True
-val indít végtelen ciklust, ennek a kifejezésnek a kiértékelése nem terheli a CPU-t.
A break
-es példám annyira nem, de a continue
eléggé izzadságszagú, nem véletlenül: általában ki lehe kerülni a break
és continue
használatát.
Tömb elemeinek kiírása
Zárásul írjunk egy ciklus ami kiírja egymás alá egy tömb elemeit:
names = ['Alice', 'Bob', 'Carol']
index = 0
while index < len(names):
print(names[index])
index = index + 1
Újdonság a 4. sorban lehet: a len()
függvény. Ez a függvény visszaadja a neki adott objektum hosszát. list
esetén ez az elemek száma. Ha tudjuk hogy mennyi elem van a tömbben, akkor nagyon egyszerűen indítunk egy ciklus, ami egyesével elszámol addig, ez kerül az index
változóba, és ezzel a változóval indexszeljük a tömböt. Ennyi az egész.
Talán mire leíram a forráskódot már a ti fejetekben is összeállt a probléma megoldása, aminek nagyon örülök, és tök jó. Pythonban viszont soha, de soha nem így járjuk be a tömb elemeit. Hadd mutassam meg nektek a valóságot.
A for ciklus
A for
ciklus a Python programozási nyelvben úgynevezett iterálható objektumok (iterables) bejárására (iterálására, iterate) való. Azok az objektumok amiken értelmezve az index operátor általában iterálatóak is. Ilyen a list
, a dict
és a str
is.
Akik más programozási nyelvekből már ismerik a
for
ciklust, azoknak ez szokatlan lehet.
Vegyük a legutolsó példát: írjuk ki egy tömb elemeit for
ciklussal:
people = ['Alice', 'Bob', 'Carol']
for name in people:
print(name)
Azt a kurva azért ez nem kicsit szexibb. A for
ciklus feje két részből tevődik össze:
- A
for
ésin
között szerepel annak a változónak (vagy változóknak!) a neve, amin keresztül el akarjuk érni az aktuális elemet (ez a példában aname
nevet kapta). Itt nem szerepelhet másféle kifejezés. - Az
in
után következik az objektum neve (vagy tetszőleges kifejezés), amit iterálunk (a példában ez apeople
változó)
Iteráljunk egy olyan list
-et, ami list
-eket tartalmaz.
people = [[1990, 'Alice'], [1991, 'Bob'], [1992, 'Carol']]
for name in people:
print(name)
A képernyőn kis két elemű tömbök jelennek meg, tehát a for
nem mászik bele a belső tömbökbe.
A Pythonban lehetőség van rá, hogy egy tömb elemeit egy sornyi változóba kicsomagoljuk, ez az unpacking. A módszer a következő:
people = [[1990, 'Alice'], [1991, 'Bob'], [1992, 'Carol']]
for year, name in people:
print(year, name)
Így az első iterációban a [1990, 'Alice']
-ból az 1990
-et a year
, az 'Alice'
-t pedig a name
változóba vezethetjük, és így tovább a későbbi iterációk során.
Nézzünk meg egy str
-t is:
for c in 'Python':
print(c)
A str
iterálása során karakterenként járjuk végig az objektumot. Tehát a képernyőn egymás alatt jelennek meg a Python szó betűi.
Nézzünk egy dict
-et is:
people = {1990: 'Alice', 1991: 'Bob', 1992: 'Carol'}
for year in people:
print(year, people[year])
A dict
iteráláskor a dict
kulcsait járja be a ciklus. Azért hogy látszódjanak a kulcsokhoz tartozó értékek is, kézzel meg kellett indexelni people
objektumot.
Lehetséges azonban az is, hogy egyszerre iteráljuk a kulcsot és az értéket is. Ehhez a kód:
people = {1990: 'Alice', 1991: 'Bob', 1992: 'Carol'}
for year, name in people.items():
print(year, name)
A year, name
bizonyára már ismerős. A people.items()
viszont tök új. Ez egy úgynevezett metódus (method), ami párokban vissza adja a people
elemeit, e miatt írhatunk unpackinget a for
és in
közé.
Erről a metódus témáról egyenlőre ennyit elég tudni, de sejthető, hogy ez valami függvény-szerűség, hisz nagyon hasonlít rájuk. Nagyon sokat fogunk még -nem is olyan túl sokára- beszélni erről.
A
for
is csak egy ciklus, így használhatóak benne abreak
éscontinue
utasítások.
A range()
Levezetésnek bemutatnék egy új függvényt, a range()
-et. Ez a függvény szám-intervallumok generálására való.
A while
-os példákban többször is használtuk a counter
változót, amit deklarálni és növelni kellett, más különben nem működött volna a ciklusunk normálisan. Ha ilyen számlálós ciklusokat akarunk írni, akkor a for
-al és a range()
-el gyorsan és kényelmesen letudhatjuk a problémát.
Írjuk ki így a számokat 1-10-ig:
for counter in range(1, 11):
print(counter)
Igen, ennyi az egész.
A függvény 3 féle argumentumot tud fogadni, de mindig csak int
értékek használhatóak. Itt egy pár példa az összes esetre:
range(5)
: Előállítja a számokat 0-4-ig. Olyan mint ha azt írnánk hogy[0, 1, 2, 3, 4]
.range(5, 10)
: Előállítja a számokat 5-9-ig. Olyan mint ha azt írnánk hogy[5, 6, 7, 8]
.range(5, 10, 2)
: Előállítja a számokat 5-9-ig kettesével. Olyan mint ha azt írnánk hogy[5, 7]
.
A 3. argumentum lehet negatív szám is, ilyenkor visszafelé számolunk. Természetesen ekkor az első értéknek nagyobbnak kell lennie a másodiknál:
for i in range(10, 0, -1):
print(i)
Így tudunk 10-től 1-ig vissza számolni egyesével.
Ha
print()
-el kiírjuk arange()
függvény visszatérési értékét, akkor meglepő módon azt kapjuk a konzolba hogyrange()
. Ez azért van, mert valójából arange()
nem tér vissza semmilyenlist
-el, amit afor
iterálhat, hanem ő maga egy olyan objektum, ami minden iterációban más értéket produkál. Majd mi is írunk ilyet csodát nemsokára.
Zárás
Azt, amikor olyan programot írunk, amiben egymást követő utasítások, elágazások és ciklusok vannak, struktúrált programozásnak (structured programming) nevezik. Python nyelv más stílusú programozást is támogat, mint pl.: a funkciónálist vagy az objektum-orientáltat. Azzal viszont hogy idáig eljutottunk, nem csak a Python, de a legtöbb programozási nyelv alapjait is megismertük.
A következő fejezetben mélyítjuk a Pythonos típusokról szerzett tudásunkat úgy, hogy azokat hasznosítani tudjuk a ciklusokban és elágazásokban.
Ez a bejegyzés a Python tutorialom egyik része. Az összes rész listája itt fellelhető.
-slp
Nincsenek megjegyzések:
Megjegyzés küldése