Csaj kiválogató
Eléggé gagyi nevet találtam ki a következő -eléggé régi- scriptemnek. Talán a 'pussy collector' jobban hangzik ,)
Az alábbi szituációval valószínűleg már sokan találkoztatok:
1- Bekerülsz egy csodás egyetemre/főiskolára
2- Beülsz az első órádra, akkor még mindenki szorgalmasan ott van. Körbenézel, és meglátsz >=1 jó csajt
3- Ki kéne deríteni a nevét
4- Felmész Neptunra vagy ETR-re és lekéred a hallgatói névsort
5- Meglátod hogy 9001 hallgató van az adott órán
6- Bazdmeg
Velem is megtörtént ez, többször is. Mivel lusta vagyok végigolvasni több száz nevet ezért inkább írtam a problémára egy programot. Szerintem nem is nehéz kitalálni a működését:
1- Olvassuk be az összes női nevet valami fájlból
2- Olvassuk be az összes hallgató nevét
3- Nézzük meg hogy a hallgató keresztneve szerepel-e a női nevek közt
4- ??????
5- PROFIT!
A legnagyobb probléma a női nevek beszerzése. Én az MTA oldalán található listát használtam fel erre. A listát valahogy át kell alakítani olyan formátumba, hogy Python is tudja olvasni. Jó persze akár maradhatna .pdf-ben is, én inkább átmásolgattam az összes nevet és soronként beillesztettem őket egy .txt fájlba. Ez még mindig gyorsabb mint elolvasni 100 nevet.
A következő lépés a hallgatók nevének összeszedése. Én idáig csak Neptunnal találkoztam, ott ki lehet exportálni .xls fájlba a névsort. Ha megvan a fájl akkor az abban szereplő neveket is belenyomkodom egy .txt fájlba.
Varázslat
#coding: utf-8
result = file("result","w")
with file("names","r") as f:
names = set(x.strip().lower() for x in f)
with file("data","r") as data:
print >> result, ''.join(filter(lambda x: x.split()[1].lower() in names, data))
Lássuk mi történik:
#coding: utf-8
result = file("result","w")
Megmondjuk hogy milyen kódolása lesz a python kódunknak. Ez után létrehozunk egy fájlt "result" néven. Ide kerülnek majd a kapott nevek.
with file("names","r") as f:
names = set(x.strip().lower() for x in f)
Ez a kis sor olvassa be és dolgozza fel a női neveket (a neveket a 'names' fájlból várja)
Ez egy eléggé elegáns módja a fájlok kezelésének Pythonban. A 'with' kulcsszó lényege az, hogy automatikusan meghívja a tőle jobbra álló objektum nyitó- és záró metódusait ráadásul ha a with alatti blokkban exception keletkezik akkor is meghívódik a záró metódus. Esetünkben a with tehát megnyitja és majd le is zárja a fájlt. A fájlra az 'f' változón keresztül tudunk hivatkozni.
A női neveket egy 'names' nevű halmazba rakom bele. Esetünkben nem fontos hogy milyen sorrendbe szerepelnek a női nevek a memóriában, ezért felesleges lenne list-et használni. Ezzel a kis odafigyeléssel nálam a kód 10x gyorsabban futott le.
A set() egy generátor kifejezést kap. A kifejezés a következő:
-Iteráljuk végig az 'f' fájlt. A fájl sorai kerüljenek bele az 'x' változóba
-Az 'x' változó végéről vágjuk le a \n\r karaktereket és alakítsuk át a szót csupa kisbetűssé
-A kapott értéket hozzáfűződik a names halmazhoz
Azért alakítom át lowercase-re az összes nevet hogy később ne legyen probléma abból hogy a milyen a kezdőbetűje a vizsgált névnek (érthető, 'A' != 'a' !)
with file("data","r") as data:
print >> result, ''.join(filter(lambda x: x.split()[1].lower() in names, data))
Ugyan úgy kezeljük a fájlt mint előbb. Most a 'data' nevű fájl tartalmát olvassuk be, ebbe szerepelnek a Neptunból letöltött hallgatók.
A with-en bellüli blokk már eléggé izgalmas.
A fájlba íráshoz a print-et használtam. A '>>'-től jobbra levő objektum .write() metódusa kerül meghívásra és automatikusan átadódik neki értékül a vesszőtől jobbra levő érték.
A vesszőtől jobbra egy egyszerű string található. A string .join() metódusa végig iterálja a neki átadott objektumot, és a kapott értékeket összefűzögeti (az értékek közé elhatároló karakternek bekerül az a string amin a .join() metódust meghívtuk. Ez esetünkben egy üres string, tehát az értékek közt nem lesz semmi elhatároló karakter).
A joinon belüli filter() függvény egy list-el tér vissza. A join majd ezt fogja végig iterálni.
A filter() megkapja a hallgatók névsorát direktbe, ez szerepel a 'data' változóban. Ez most kivételesen a második argumentum. Az első argumentum az a függvény ami a szűrés feltételét tartalmazza. Esetünkben most egy lambda kifejezés található. A működése eléggé egyszerű:
1- A lambda az 'x' változóban kapja meg a fájl egyes sorait
2- Szétszeleteljük a sort, és kivesszük a kapott tömb 1. indexén levő értékét. A szeleteléssel gyakorlatilag részeire bontottuk az aktuális nevet. A 0. indexen így a vezetéknév van, felette pedig a keresztnevek. Feltételezem hogy nincs olyan női név hogy "Kovács Bélda Dóra", tehát hogy a 2. részről már el lehet dönteni hogy nőről van-e szó.
3- Átalakítjuk a keresztnevet kisbetűssé -mert a női névsor a program elején is ilyenre lett állítva
4- Az 'in' utasítás megnézi hogy szerepel-e az aktuális keresztnév a női nevek közt. Ha igen akkor ez a kifejezés értelemszerűen True-t ad vissza, és a filter szépen berakja egy list-be.
Fontos azt megjegyezni, hogy az éppen vizsgált név végén a lezáró \n\r karakterek ott szerepelnek, viszont mivel azt szeretnénk hogy a kimeneti fájlban is soronként legyenek a nevek ezért felesleges levágnunk őket. Amikor a join összefűzi őket akkor a kapott stringben szerepelni fognak ezek a karakterek, a fájlba írásnál pedig mindenki új sorba kerül majd, ahogy kell.
Konklúzió
A kódot összerakni nagyjából 10 percet vett igénybe nekem, ebbe benne volt a névsorok előbányászása is feldolgozása is. Ennyi idő alatt végig is olvashattam volna a listát, viszont ha legközelebb is át kell néznem pár száz nevet akkor csak előszedem ezt a kis kódot, betáplálom neki a névsort (hisz a női nevek listája már megvan) és kész is.
-slp
Érettségi Pythonban
Pont ma jutott eszembe, hogy a következő bejegyzésem valamilyen emelt informatikai érettségi programozás részének a megoldása lesz pythonba, erre kiderült hogy 2012-ben Pythonban is lehet majd programozni az érettségin. Ez nagyon jó, hisz így pofon egyszerű lesz leérettségizni.
Én 2010-ben érettségiztem informatikából (közép szinten), úgyhogy stílusosan most megoldanám az akkori emelt feladatot itt pythonban. (Érdekes: direkt azért nem jelentkeztem emelt érettségire mert egyáltalán nem tudtam programozni és el is döntöttem nagyjából hogy soha nem is fogom tudni megtanulni..)
Lássuk tehát a 2010 májusi emelt infó programozás részének feladatkiírását
Egy autóbuszokat üzemeltető társaság távolsági járataira az utasok jobb kiszolgálása érdekében csak akkor ad el jegyet, ha ülőhelyet is tud biztosítani. Minden jegyre rányomtatja, hogy az adott vonalon mettől meddig érvényes és melyik ülést lehet elfoglalni birtokában.
Az eladott.txt állomány pontosan egy út jegyvásárlásait tartalmazza. Az első sorban az eladott jegyek száma (legfeljebb 500), a vonal hossza (legfeljebb 200 km) és minden megkezdett 10 km után fizetendő összeg (legfeljebb 100 Ft) található.
Az állomány további sorai — a vásárlás sorrendjében — egy-egy jegy három adatát írják le: az utas melyik ülést foglalhatja el, hol száll fel és hol száll le. (A fel- és a leszállás helyét a járat kezdőállomásától mért távolsággal adják meg.) Az üléseket 1-től 48-ig folyamatosan számozták. A soron belüli határoló jel minden esetben egy-egy szóköz. Az állomány csak egész számokat tartalmaz.
Az utast a későbbiekben egyetlen sorszámmal azonosítjuk, azzal az értékkel, amely megadja, hogy hanyadik jegyvásárló volt.
A jegy árának meghatározásakor az értéket öttel osztható számra kell kerekítenie. (1, 2, 6 és 7 esetén lefelé, 3, 4, 8 és 9 esetén pedig felfelé kell kerekítenie.)
Direkt link a feladatlaphoz , itt pedig a forrásfájlok.
Lustaságom miatt sajnos sok dolog a megoldás során ismeretlen fog lenni, de ez a bejegyzés nem a tutorial része ugye.
Először is értelmezzük a feladatot:
Van egy fájlunk, ez az eladott.txt (forrásfájlok közt megtalálható). Az első sora az összes eladott jegy száma, utána a teljes vonal hossza, végül hogy mennyibe kerül 10km-nyi utazás. A feladat kiírás szépen hangsúlyozza hogy ezek az értékek maximum mekkorák lehetnek, így könnyen tudnánk típust választani hozzájuk ha C-ben programoznánk, de ez most Python ugye, úgyhogy elhagyhatjuk ezeket a pontokat.
Az első sor -hívjuk header-nek- után következnek a jegyvásárlások, ezt három adat alkotja. Az első az ülés sorszáma amit az utas kapott, a második hogy hányadik kilóméternél szállt fel, és az utolsó hogy hányadiknál szállt le. Ülésekből 48 db van, az első sorszáma 1, az utolsóé meg 48. Az adatok közti határoló karakter sima szóköz, az értékek pedig egészek (int).
Az utasok a szerint kapnak majd azonosítót, hogy hányadik vásárlók voltak. Ugye a fájl minden sora egy-egy vásárlás, így gyakorlatilag minden sor egy-egy utas is.
A kiírás külön kitér rá hogy öttel osztható pénz értékekkel dolgozzunk -ugye eltörölték az 1 & 2 forintos érméket). Erre majd külön függvényt fogunk írni.
A feladat kiírás ez után egy példát mutat, az ide már nem kerül beszúrásra. Arra még oda kell figyelni hogy ha a programunk kiír valamit a képernyőre, akkor jelezzük hogy az hanyas részfeladatban történik plusz illedelmesen szólítsuk fel a felhasználót ha adatokra lenne szükségünk : )
Kétféle megoldást szeretnék tálalni, az első a 'fapadosabb', az utolsó meg a 'pythonosabb' (vagy 'kibaszósabb' :).
Szépen tételesen haladunk végig, és a leírás végén rakom össze egybe a kódot.
1. Olvassa be az állományt. Ha nem sikerül akkor gépelje be az első 10 sort.
Ahogy feljebb írtam, a fájl első sora a header, ezt külön kell kezelnünk. Esetünkben soronként fogjuk beolvasni a fájlt a programba, az adott sort szétvágjuk, és szétpakoljuk a benne levő értékeket.
Még mielőtt akárminek is neki állnánk, adjuk meg a python fájl karakterkódolását, nehogy e miatt pontot veszítsünk.
# -*- coding: utf-8 -*-
adat = open('eladott.txt','r')
header = adat.readline().strip()
infok = header.split(' ')
Ezzel meg is történt az első sor beolvasása. A .strip() eltávolítja a stringből a \n\r karaktereket. A .split() metódus szétvágja a stringet a megadott karaktereknél, és tömbként visszaadja a szétvágott stringet. Fontos: a beolvasás string-ként történt, így hogy számolni tudjunk majd át kell alakítani az adatainkat int-ekké.
Az infok list-be bekerült hogy:
- mennyi jegyet adtak el (113)
- milyen hosszú úton közlekedik a járat (172)
- és hogy 10km-nyi út mennyi forintba kerül (71)
A jobb olvashatóság érdekében kirakjuk ezeket az értékeket külön változókba, és ekkor el is végezzük a típuskonverziót.
# -*- coding: utf-8 -*-
adat = open('eladott.txt','r')
header = adat.readline().strip()
infok = header.split(' ')
jegyek = int(infok[0])
uthossz = int(infok[1])
km_ar = int(infok[2])
Ezzel elvégeztük a header teljes feldolgozását. Most következik a többi sor értelmezése.
A sorok felépítése a következő innentől: ülés, felszállás, leszállás. Egy tömbbe fogjuk tárolni az összes értéket. Ugye pythonba a tömb mérete teljesen dinamikus, ezért semmilyen problémánk nincs. C-ben most kéne annyi memóriát foglalni, hogy minden tétel beleférjen, a tételek mennyisége pedig a jegyek változóban van, de ez egy másik történet.
utazasok = []
for sor in adat:
sorTemp = sor.strip().split(' ')
utazasok.append([int(sorTemp[0]),int(sorTemp[1]),int(sorTemp[2])])
adat.close()
Elsőre kicsit ronda, mert igazából az is. Az utazásokat az 'utazasok' tömbbe rakjuk. Szépen soronként végig iteráljuk a fájlt. Arról már itt is volt szó, hogy a for az egy iterátor. A fájl objektumunk iterálható, és minden hívással a fájl egy újabb sorát adja vissza, ezért lehet ilyen szép elegánsan végig járni.
A ciklus magjában egy átmeneti változóba kerül a vezérlőkarakterektől megfosztott és szétvágott sor.
Ezek után az 'utazasok' tömbhöz hozzáfűzünk egy-egy tömböt. A tömb elemei a sor egyes értékei integerré alakítva. Ezekből három van ugye, a jelentésüket pedig feljebb írtam.
Az egész procedúra után lezárhatjuk az 'adat' változóba behúzott fájlunkat, mivel minden adat már a memóriában van.
A teljes kód tehát:
# -*- coding: utf-8 -*-
adat = open('eladott.txt','r')
header = adat.readline().strip()
infok = header.split(' ')
jegyek = int(infok[0])
uthossz = int(infok[1])
km_ar = int(infok[2])
utazasok = []
for sor in adat:
sorTemp = sor.strip().split(' ')
utazasok.append([int(sorTemp[0]),int(sorTemp[1]),int(sorTemp[2])])
2. Adja meg a legutolsó jegyvásárló ülésének a sorszámát és az általa beutazott távolságot.
Remek, végre agyalni is kell valamin! Az utazasok tömbbe benne van minden adat, a beolvasás sorrendje szerint. A fájl legutolsó sora a legutolsó jegyvásárló. Ennek a sornak az első értéke az ülésének sorszáma. Tehát az 'utazasok' tömb utolsó eleme kell. Itt egyszerűen a fordított indexelést fogjuk használni, mert megtehetjük. Kiszámolni majd azt kell, hogy mekkora távolságot utazott be. Ez értelem szerűen a leszállási és a felszállási kilométerek különbsége. Akkor most az egész egybe:
#2. feladat #Utolso utas es a megtett tavolsaga utolso_utas = utazasok[-1] utolso_ulese = utolso_utas[0] tavolsaga = utolso_utas[2]-utolso_utas[1] print "2. feladat" print "Az utolsó utas a %s ülésen ült és %skm-t tett meg" % (utolso_ulese,tavolsaga)
Remélem azért másnak is kicsit zavaró ez a sok változó. Nekem már annyira régen kellett így írnom, hogy eléggé nehezen jut eszembe hogy hogyan tudom még egyértelműbbé tenni a kódomat. Sebaj.
Az utazások közül kivettük külön az utolsó utas adatait. A tömb utolsó elemét a -1 indexszel értük el. Ez után külön változóba kerül ennek az utasnak az ülésének a száma, és az általa megtett távolság.
A távolság esetleg nem lehet egyértelmű valakinek, szóval:
A busz 0km-től halad 172km felé. Ezek szerint a tömbbe először egy kisebb aztán egy nagyobb érték van minden esetben. Mivel ezt tudjuk, és nem akarunk -1-el szorozgatni, így a nagyobb értékből vonjuk ki a kisebbet, így mindig helyes értéket kapunk.
Miután minden érték megszületett szépen kiírjuk az eredményt, format stringgel ahogy kell.
3. Listázza ki, kik utazták végig a teljes utat! Az utasok sorszámát egy-egy szóközzel elválasztva írja ki a képernyőre!
Ha jól dekódoltam a kérdést, akkor azokat kell megszámolnunk, akik 0km-nél -az út legelején- szálltak fel, és az út legvégén -itt: 172km, de ez benne van egy mosolygós változóban- szálltak le.
Az ötletem tehát az, hogy bejárjuk a tömböt, megnézzük hogy melyik utas szállt fel 0km-nél, aztán azt hogy ez az utas 172-nél szállt-e le. Ha igen, akkor kiírjuk a sorszámát (ez számoljuk közben).
#3. feladat
#Teljes utat vegigutazok kivalogatasa
print "3. feladat"
print "Teljes utat végigutazók:",
counter = 1
for utas in utazasok:
if utas[1] == 0 and utas[2] == uthossz:
print counter,
counter += 1
Ebben a példában megfigyelhető a print egy érdekes használata. Mint ismeretes, a print a sor végére sortörést tesz, de ha a kifejezést vessző ',' karakterrel zárjuk, akkor nem kerül \n a sor végére, hanem marad minden egy sorban (jó, egy szóköz kerül az kiírás végére, de ez nekünk most pont jó, mert szóközökkel kellett elválasztani az eredményeket).
Mivel végig iteráljuk a tömböt, ezért külön változót kell bevezetnünk, ami számlálja hogy hányadik utasnál tartunk. Csak hogy kompatibilisek legyünk a valódi élettel ezért a számlálót 1-ről indítjuk.
A cikluson belül van a lényegi rész. Az 'if' feltétele hogy az utas[1] == 0 és utas[2] == uthossz legyen. Az utas[1] a kezdő kilométer. Ennek ebben a feladatban fixen 0-nak kell lennie. Az utas[2] a leszállás kilométere. Ide beírhatnánk akár hogy konstans 172, de mivel olyan szépen kitettük változóba hogy milyen hosszú a teljes út, ezért hasonlítsuk csak az értéket a változónkhoz. Na meg eléggé nagy a valószínűsége hogy a javítókulcs külön kitér erre.
Ha a feltételünk igaz, akkor kiírjuk a sorszámot. A ciklus végén emelgetjük a számláló változót, így fogjuk tudni a pozíciónkat az utazások közt.
Futtassuk le ezt a kódrészletet. Nálam három eredmény jelent meg, de ha megnézem őket a fájlban akkor a kapott sorszámok nem passzolnak, el vannak csúszva. Ez azért van mert a header-t külön dolgoztuk fel, ami a fájl első sora volt. Ezt a sort viszont nem kell utólag hozzá adni, mivel az utasok sorszámára vagyunk kíváncsiak, és nem a fájl összes sorának a számára. Tehát nem kell aggódni.
4. Határozza meg, hogy a jegyekből mennyi bevétele származott a társaságnak! Az eredményt írja a képernyőre!
Ebben a részben már pénzzel is kell foglalkoznunk, tehát meg kell írnunk a kerekítő függvényt. Ki kell számolni hogy hány kilométert tettek meg az egyes utasok, és hogy ez mennyibe került nekik. A végén az egészet össze kell adni, ha jól gondolom.
Először nézzük a kerekítő függvényt!
#Penz kerekito fuggveny def penz_kerekit(x): mihez = 5 return int(mihez * round(float(x)/mihez))
A kerekítéshez ezt az egyszerű kis függvényt használom. Nyugalom, nem most találtam ki, csak régebben is szükségem volt már ilyenre, így megjegyeztem. Nem tartom célszerűnek leírni magyar szavakkal hogy hogyan működik ez a függvény, kifejezi ezt a kód maga.
Most hogy megvan a függvény, következhet az utazások árának megállapítása. Ehhez végigjárjuk a tömböt, kiszámoljuk hogy milyen hosszúak voltak az egyes utak. Igen ám, de minden megkezdett tíz kilométernél felfelé kerekítjük a teljes megtett hosszt. Hétköznapian: ha valaki 51km-t utazott, az 60km-t fizet.
Ezen probléma megoldására is egy függvényt írtam, ezt most saját ötlet alapján. Eléggé kis gagyi, lássuk a kódot:
#Ut kerekito fuggveny def ut_kerekit(x): return int((x+9)/10)*10
Az alap ötlet az hogy:
-Osszuk el a számot 10-el, így az egyes helyi értéken álló számjegy tizedesjeggyé változik
-Alakítsuk át ezt a törtet int-é, ilyenkor a tizedes rész levágódik
-Szorozzuk fel a számot 10-el, hogy visszakapjuk a megfelelő nagyságú számot.
Az jó is lenne, csak az a probléma hogy lefelé kerekít. Ha a paraméterül kapott számot eltoljuk (hozzáadunk valamennyit) akkor a kerekítés helyes lesz. Logikusnak tűnik hogy 10-el növeljük az x-et, így viszont a kerek tízes kilométerekért kétszer több pénzt kell fizetni (10km-ért 2*10km-t). Ha 9-el növeljük az x-et, akkor minden a helyére kerül, és ha 0km-t tettünk meg akkor logikusan 0Ft-ot is kell fizetni érte.
Most már tényleg minden készen áll, lássuk az egészet együtt!
bevetel = 0 for utas in utazasok: megtett_tavolsag = utas[2]-utas[1] koltseg = penz_kerekit((ut_kerekit(megtett_tavolsag)/10)*km_ar) bevetel = bevetel + koltseg print print "4. feladat" print "A teljes bevetel: %s Ft" % (bevetel)
A bevétel tárolására új változót vezetünk be, ezt 0-ra is állítjuk.
Végig ugrálunk az utazások tömbjén. A cikluson belül egy átmeneti változóba bekerül a megtett távolság, ami az értkezés-indulás kilométere.
A költséget úgy kapjuk meg, hogy a megtett távolságot először felkerekítjük egész tízesre. Ez után ezt az eredményt le kell osztani 10-el, mivel az utazás egységára 10km-re vonatkozik! Az osztás után felszorozzuk az eredményt az egységárral. A kapott összeget az ötösre kerekítő csodafüggvénnyel kerekítjük öt többszörösére. Azért nem a legutolsó végeredményt kerekítjük mert minden egyes jegyvásárlás végén ötösre kerekített összeget kell fizetni. Ez persze nem volt külön kiírva, de így természetes a gondolatmenet.
A kiírás előtt van egy üres print parancs. Erre azért van szükség, mert a legutolsó feladat printje nem tette ki a sortörést (mert ','-t tettünk a végére), így hogy olvasható legyen itt írunk \n-t külön.
5. Írja a képernyőre, hogy a busz végállomást megelőző utolsó megállásnál hányan szálltak fel és le!
Ez tipikusan olyan 'érettségis' kérdés, amin csak pislogok az első öt percben.
Ha jól értem a dolgot, akkor ki kell találnunk, hogy hányadik kilométernél volt az utolsó előtti megálló (hisz az utolsó megálló az a végállomás, ott nem száll fel senki). Ez után ki kell írni hogy kik azok akik ettől a kilométertől utaznak (ők itt szálltak fel), utána pedig azokat akik eddig a kilométerig utaztak csak (ők itt szálltak le).
Első dolgunk tehát egy maximum kiválasztás. Végig kell gyalulni az utazások tömbjét, és a leszállások kilométerei közül a 2. legnagyobb érték kell nekünk.
A legegyszerűbb megoldás az lesz, ha előállítunk egy új tömböt ezen értékekből, és sorba állítjuk az értékeket, aztán egyszerűen kivesszük a tömb 2. elemét (ez lesz a 2. legnagyobb)
#5. feladat #Utolso elotti megallo megallok = [] for utas in utazasok: megallok.append(utas[2]) megallok = sorted(list(set(megallok)), reverse=True) utolso_elotti = megallok[1]
Gondolom a ciklusig nincs probléma, aztán utána hirtelen valami hányás következik. Nem tudtam megállni hogy ne oldjam meg a lehető legkényelmesebben az utolsó előtti megálló megállapítását. A gondolat:
-Sajnos a megállók közt ugyan az az elem többször is szerepel, ezeket ki kéne venni
-Átalakítom a tömböt halmaz típussá. A halmazokban egyedi elemek vannak, így a dupla értékek eldobódnak
-Vissza alakítom a halmazt tömbbé, mert a halmazok elemei közt nincs sorrendiség
-A sorted() függvény visszaadja a tömböt sorba helyezett elemekkel, de növekvő sorrendben. A reverse=True fordított rendezést végez, tehát csökkenőt
-A megállók tömb 0. eleme a végállomás, az 1. elem a végállomás előtti megálló.
Most hogy tudjuk a megálló kilométerét végigmegyünk megint a tömbön, és kiválogatjuk a fel- és leszállókat
leszallo = 0
felszallo = 0
for utas in utazasok:
if utas[1] == utolso_elotti:
felszallo += 1
if utas[2] == utolso_elotti:
leszallo += 1
print "5. feladat"
print "Felszálltak: %s | Leszálltak: %s" % (felszallo,leszallo)
Két változóba kerülnek az eredmények. Végigmegyünk az utazások tömbjén és ha az utas tömb 1. indexén levő érték egyezik meg az utolsó előtti megálló kilométerével akkor felszálló utasról van szó, ha a 2. indexű érték akkor pedig leszálló utasról. Az egész végén kiírjuk az eredményt.
6. Adja meg, hogy hány helyen állt meg a busz a kiinduló állomás és a célállomás között! Az eredményt írja a képernyőre!
Itt több minden is előfordulhat. Először is az, hogy volt felszálló, de leszálló nem, és fordítva. Ezek miatt össze kell húzni az összes megállót, persze úgy hogy ne legyen benne kétszer ugyan az az érték. Arra is figyeljünk oda, hogy 'a kiinduló állomás és a célállomás között', tehát ezeket nem szabad beleszámolni az eredménybe.
Az előző feladatban használt halmazzá-alakítós 'trükköt' vetjük be itt is a megállóhelyek összeszedésére.
#6. feladat
#Megallohelyek
megallok = []
for utas in utazasok:
for km in utas[1:]:
megallok.append(km)
osszegMegallo = len(set(megallok))-2
print "6. feladat"
print "A busz összesen %s helyen állt meg" % (osszegMegallo)
Felüldefiniáljuk az előző feladatban használt 'megallok' list-et. Szépen végig haladunk az utazásokon, és ezen belül az utasokon is.
A belső ciklus végig iterálja az utas[1:] tömböt. Remélem nem felejtette el senki, hogy az utazasok tömb elemei tömbök. Az utas[1:] tömb az utas tömb másolata az eredeti tömb első indexétől (list slice). Azaz kihagyjuk az utas tömbök első elemeit.
Erre azért van szükség, mert a tömbök 2. és 3. elemei tartalmazzák a megállókat, nekünk csak ezeket kell összeszedni. Végig iterálunk ezeken a tömbökön és hozzá adjuk a megallok tömbhöz.
Az egész végén megállapítjuk a megállók tömb méretét, ez lesz egyelő a megállók számával:
-Halmazzá alakítjuk a megallok list-et, hogy eltűnjenek a dupla értékek
-Lekérjük a halmaz méretét
-Kivonunk 2-t belőle, a kezdő- és végállomás miatt.
Ezek után egyszerűen kiírjuk az eredményt.
7. Készítsen utaslistát az út egy pontjáról. Ülésenként tüntesse fel hogy adott pillanatban melyik utas foglalja el. A pont legyen egy a kiindulástól mért távolság, és ezt az user írja be. Ha épp megállóról van szó, akkor a felszálló utasokat vegye figyelembe, a leszállókat pedig hagyja figyelmen kívül. Ülések sorrendjében írja a kihol.txt fájlba. Ahol nincs senki, ott legyen 'üres'
Na, ez szép kövér feladat, úgyhogy ezt félreteszem holnapra.
*másnap*
Jó, elsőre nem teljesen világos hogy mi történjen akkor ha a felhasználó olyan kilométert ütött be ahol megálló van, úgyhogy egyelőre összeszedjük amiben biztosak vagyunk:
-Bekérjük az usertől a kilométert
-Végigmegyünk a tömbön és kiválogatjuk azokat akik épp utaznak
-Indítunk egy számlálót 1-től 48-ig (ezek az ülések számai) és valamelyik ülésen ül valaki akkor kiírjuk a sorszámát, amúgy meg azt hogy 'üres'
Szerintem ezt a feladatot is úgy tudjuk a legegyszerűbben megoldani ha tömbbe kirakjuk a megfelelő utasokat. Lássuk is a kódot:
print "7. feladat"
print "Melyik kilométer utaslistáját akarja kimenteni?"
ut_km = int(raw_input())
utaslista = {}
sorszam = 1
eredmeny_fajl = open('kihol.txt','w')
for utas in utazasok:
if utas[2] > ut_km >= utas[1]:
utaslista[utas[0]] = sorszam
sorszam += 1
Először megkérjük a kedves felhasználót hogy írja be hogy hányadik kilométer utaslistájára kíváncsi. Ezt csinálja a raw_input() aminek értékét azonnal int-é is alakítjuk. Az ezen a kilométeren levő utasok listáját asszociatív tömbbe (dict) tároljuk, mert ez így tök egyszerű. A 'sorszam' változóba az utas sorszáma kerül, ahogy a 3. feladatban (ott counternek neveztem el).
Fontos még -nekem utólag jutott eszembe- hogy az eredmény listát fájlba kell írni. Az open() függvénnyel megnyitjuk a 'kihol.txt' fájlt írásra ('w').
Végig pattogunk az 'utazasok' tömbön, és itt jön a lényegi rész.
Onnan tudjuk hogy egy utas épp utazik hogy a beírt kilométer az utas kezdő- és vég kilométere közé esik.
Lássuk példával:
-Az 1. utas 6km-nél szállt fel, és 16km-nél száll le
-A felhasználó beírta hogy 10km
-Az első utas 6-16km közt utazott. Ebbe az intervallumba beleesik a 10km, így ő épp utazott a 10. km-nél.
-Ezt berakjuk a dict-be.
A feladat kiírás szerint ha a beírt kilométer épp megálló akkor a felszállókat vegyük figyelembe, és a leszállókat ne. A felírt feltétel kicsit átalakítva így hangzik:
- leszállás kilométer > beírt kilométer >= felszállás kilométer
Mivel a felszállókat akarjuk figyelembe venni, ezért ezen az oldalon megengedjük az egyenlőséget, a másik oldalon pedig nem, így lesz helyes a feltételünk.
Az asszociatív tömb kulcsa az adott utas ülésének száma lesz. Használhatnánk akár sima list-et is, de egyáltalán nem valószínű hogy minden helyen ülnének emberek, így lukak keletkeznének az eredmény tömbjébe. Persze lehetne ellenőrizgetni majd amikor az üléseken végig haladunk hogy az adott ülés szerepel-e az utazók tömbjébe, de ez sokkal macerásabb.
Ha asszociatív tömböt használunk akkor egyszerűen megtudjuk majd amikor az üléseken ugrálunk hogy az adott ülésen ül-e valaki (az 'in' paranccsal). De lássuk inkább a kódot:
for i in range(1,49):
if i in utaslista:
eredmeny_fajl.write("%s. ülés: %s. utas\n" % (i,utaslista[i]))
else:
eredmeny_fajl.write("%s. ülés: üres\n" % (i))
eredmeny_fajl.close()
Elindulunk egy for ciklussal, 1-től 49-ig. Ugye a range() függvény olyan tömböt ad vissza amiben az első operandus benne van, de az utolsó már nincs. Ezért mivel 48 ülés van a buszban így 48+1-ig kell elszámolnunk. Az 'i' változó reprezentálja majd az ülés sorszámát.
A feltételünk pofon egyszerű. Ilyen formában ahogy le van írva az 'in' utasítás arra ad választ, hogy szerepel-e az adott kulcs az asszociatív tömbben. Ha ez a feltétel igaz, akkor beleírjuk a fájlba az ülés sorszámát és hogy hányadik utas ül azon a helyen.
Ha nincs benne a kulcs a dict-ben, akkor az ülés helyére azt írjuk hogy 'üres'
Az egész végén lezárjuk a fájlt. Igaz hogy a program itt a végéhez érkezett, de e miatt ne érje szó a ház elejét.
8. Készen vagyunk
Úgy néz ki hogy elkészültünk az alap kóddal, így akkor most az egészet egybe
# -*- coding: utf-8 -*-
#1. feladat
#Beolvasas
adat = open('eladott.txt','r')
header = adat.readline().strip()
infok = header.split(' ')
jegyek = int(infok[0])
uthossz = int(infok[1])
km_ar = int(infok[2])
utazasok = []
for sor in adat:
sorTemp = sor.strip().split(' ')
utazasok.append([int(sorTemp[0]),int(sorTemp[1]),int(sorTemp[2])])
#2. feladat
#Utolso utas es a megtett tavolsaga
utolso_utas = utazasok[-1]
utolso_ulese = utolso_utas[0]
tavolsaga = utolso_utas[2]-utolso_utas[1]
print "2. feladat"
print "Az utolsó utas a %s ülésen ült és %skm-t tett meg" % (utolso_ulese,tavolsaga)
#3. feladat
#Teljes utat vegigutazok kivalogatasa
print "3. feladat"
print "Teljes utat végigutazók:",
counter = 1
for utas in utazasok:
if utas[1] == 0 and utas[2] == uthossz:
print counter,
counter += 1
#Penz kerekito fuggveny
def penz_kerekit(x):
mihez = 5
return int(mihez * round(float(x)/mihez))
#Ut kerekito fuggveny
def ut_kerekit(x):
return int((x+9)/10)*10)
bevetel = 0
for utas in utazasok:
megtett_tavolsag = utas[2]-utas[1]
koltseg = penz_kerekit((ut_kerekit(megtett_tavolsag)/10)*km_ar)
bevetel = bevetel + koltseg
print
print "4. feladat"
print "A teljes bevetel: %s Ft" % (bevetel)
#5. feladat
#Utolso elotti megallo
megallok = []
for utas in utazasok:
megallok.append(utas[2])
megallok = sorted(list(set(megallok)), reverse=True)
utolso_elotti = megallok[1]
leszallo = 0
felszallo = 0
for utas in utazasok:
if utas[1] == utolso_elotti:
felszallo += 1
if utas[2] == utolso_elotti:
leszallo += 1
print "5. feladat"
print "Felszálltak: %s | Leszálltak: %s" % (felszallo,leszallo)
#6. feladat
#Megallohelyek
megallok = []
for utas in utazasok:
for km in utas[1:]:
megallok.append(km)
osszegMegallo = len(set(megallok))-2
print "6. feladat"
print "A busz összesen %s helyen állt meg" % (osszegMegallo)
#7. feladat
#Utaslista
print "7. feladat"
print "Melyik kilométer utaslistáját akarja kimenteni?"
ut_km = int(raw_input())
utaslista = {}
sorszam = 1
eredmeny_fajl = open('kihol.txt','w')
for utas in utazasok:
if utas[2] > ut_km >= utas[1]:
utaslista[utas[0]] = sorszam
sorszam += 1
for i in range(1,49):
if i in utaslista:
eredmeny_fajl.write("%s. ülés: %s. utas\n" % (i,utaslista[i]))
else:
eredmeny_fajl.write("%s. ülés: üres\n" % (i))
eredmeny_fajl.close()
A biztonság kedvéért letöltöttem utólag ennek a feladatnak a c++-os megoldását. Hosszra nem sokkal vagyunk rövidebbek, kicsit meg is lepődtem. A lényeg hogy minden eredmény passzol a példaprograméval.
Ahogy ígértem, akkor most kicsit cifrázzuk a fenti kódot, csak hogy pythonosabb legyen.
# coding: utf8
#1. feladat
#Beolvasas
adat = open('eladott.txt','r')
jegyek, hossz, km_ar = [int(item) for item in adat.readline().strip().split(' ')]
utazasok = [[int(data) for data in row.strip().split(' ')] for row in adat]
penz_k = lambda x: int(5 * round(float(x)/5))
ut_k = lambda x: int((x+9)/10)*10
#2. feladat
#Utolso utas es a megtett tavolsaga
utolso_utas = utazasok[-1]
print "2. feladat"
print "Az utolsó utas a %s ülésen ült és %skm-t tett meg" % (utolso_utas[0],
utolso_utas[2]-utolso_utas[1])
#3. feladat
#Teljes utat vegigutazok kivalogatasa
totals = ' '.join([str(utas[0]+1) for utas in enumerate(utazasok) if utas[1][1] + utas[1][2] == hossz])
print "3. feladat"
print "Teljes utat végigutazók: %s" % (totals)
#4. feladat
#Teljes bevetel
bevetel = sum(penz_k((ut_k(utas[2]-utas[1])/10)*km_ar) for utas in utazasok)
print "4. feladat"
print "A teljes bevetel: %s Ft" % (bevetel)
#5. feladat
#Utolso elotti megallo
utolso_e = sorted(list(set(utas[2] for utas in utazasok)), reverse=True)[1]
fel = [utas for utas in utazasok if utas[1] == utolso_e]
le = [utas for utas in utazasok if utas[2] == utolso_e]
print "5. feladat"
print "Felszálltak: %s | Leszálltak: %s" % (len(fel),len(le))
#6. feladat
#Megallohelyek
megallok = len(set(m for utas in utazasok for m in utas[1:]))-2
print "6. feladat"
print "A busz összesen %s helyen állt meg" % (megallok)
#7. feladat
#Utaslista
print "7. feladat"
ut_km = int(raw_input("Melyik kilométer utaslistáját akarja kimenteni? "))
utaslista = {utas[0]:i+1 for i, utas in enumerate(utazasok) if utas[2] > ut_km >= utas[1]}
kihol_txt = open('kihol.txt','w')
for i in range(1,49):
if i in utaslista:
print >> kihol_txt, "%s. ülés: %s. utas" % (i,utaslista[i])
else:
print >> kihol_txt, "%s. ülés: üres" % (i)
Ez így máris sokkal rövidebb és persze menőbb is. Na de a pontos működését most nem fogom leírni, mert az még egyszer ennyi szöveg lenne.
Szerintem fogok még ilyen feladatokat csinálni, bár amilyen a szorgalmam..
-slp
indentek megtartása
Jó régen írtam megint, és most nem is annyira tutorialt, inkább egy kis megjegyzést írnék a blogban megjelenő kódokra.
Freeblogon eléggé nehéz színezni a kódot, értsd: sok pöcsöléssel jár. Ha egyszer megírtam egy bejegyzést, és észreveszek benne egy hibát, akkor kb szétgányolja az összes kódot a szerkesztés során. Lehet hogy szimplán én vagyok a balfasz, akkor valaki árulja már el hogy hogyan kell ezt jól csinálni.
Na de a fő probléma, hogy a kódok többnyire úgy módosulnak hogy:
-a végére kerül egy <br> tag
-felbomlik a sorok behúzása
Igyekszem azért kijavítani a hibákat, de ha valamelyik kód nem fut, és nem értitek a hibaüzenetet, akkor kezdjétek úgy az egészet hogy megnézitek hogy a sor végén nincs-e véletlenül '<br>' tag, és hogy jól van-e mindenhol behúzva a kód.
-slp
python sebesség 1
Engem nagyon foglalkoztat hogy a kód amit megírok mennyire gyorsan fog tudni majd lefutni. A Python eléggé magas szintű nyelv, tehát egy adott pontnál gyorsabb semmiképp se lehet, de azért csak igyekezzünk kihozni a maximumot belőle.
Én a kivételkezelést azzal a példával szoktam szemléltetni, hogy a program kifejezetten szám begépelésére szólítja fel az usert, és hogy hogyan "kapjuk el" azt az esetet, amikor valahogy mégse sikerül egy nyamvadt számot begépelni.
Most nem fogok kitérni a kivételkezelésre, mivel ez a post nem is 'a' tutorial része.
A kérdés az lenne, hogy hogyan lehet felgyorsítani annak az eldöntését, hogy a felhasználó hajlandó volt-e számot begépelni nekünk.
Az alap szituáció:
A begépelt adatot az int() függvénnyel számmá alakítjuk. Ez a függvény ValueError exceptiont dob (vagyis inkább "emel fel", mivel pythonban az exceptiont raise-olni szokás (más nyelvekben throw-olni)) ha a paraméteréül kapott érték nem szám. Akkor talán kezdjük ezzel.
A "begépelést" egy konstans érték vizsgálata fogja helyettesíteni mert ez kényelmes, meg mert így a program lefutásának idejébe nem zavar be a begépelésre szánt idő.
Két teszteset lesz, az elsőben a tesztelt adat valóban szám lesz, a másodikban pedig egy string.
Minden tesztet 5x fogok lefuttatni, ezek átlaga lesz az eredmény.
Tesztkörnyezet:
CPU: Intel Core i5 750 @ 3000MHz
1 - Alap kód (int.py):
import time
start = time.time()
a = "314159265358979323"
for z in xrange(1000000):
try:
int(a)
except ValueError:
pass
print time.time()-start
A program elején eltároljuk a futtatás megkezdésének az idejét a start változóba. Az a változóba kerül a pi első 18 számjegye egy stringként. Mint ismeretes a raw_input mindent stringként ad vissza, így ez teljesen realisztikus.
A for ciklus 1.000.000x fut le. Egy szimpla vizsgálat olyan gyorsan fut le, hogy nem lehetne lemérni. Így kvázi 1milliószorosára növeltük a futásidőt.
A cikluson belül csak annyi történik, hogy megpróbáljuk az a változót int-é alakítani. ValueError típusú exception esetén tovább haladunk (más exceptionra nem számítunk, ezért csak ez az egy van felsorolva)
A futás végén az aktuális idő és a futás kezdetének a különbségét kapjuk meg, tehát a program teljes futásidejét.
2 - Alap kód (string.py)
import time
start = time.time()
a = "314159265358979323e"
for z in xrange(1000000):
try:
int(a)
except ValueError:
pass
print time.time()-start
Annyira nem nagy a különbség a két kód közt csupán annyi lenne, hogy az 'a' változó értékének a végére került egy 'e' karakter. Így máris nem lehet int-é alakítani ezt a változót.
A továbbiakban nem írnám ki mindig a két kódot, csak az elsőn végzett módosításokat.
Nézzük egyből a kód futásidejeit:
int.py: 0.883s
string.py: 2.217s
Úgy néz ki hogy 2.5x tovább tart lekezelni azt az esetet, amikor a program nem integerrel dolgozik. Ez érthető hisz meg kell várni hogy az int() exceptiont dob-e, ha igen, akkor át kell ugrani az except ágba, megvizsgálni hogy elkapjuk-e azt az exceptiont amit az int() dobott, és ha igen akkor jöhet a pass. Ez a procedúra természetesen 1milliószor.
4 - ValueError elhagyása
Úgy szép ha az exception ágba megmondjuk pontosan, hogy milyen hibát akarunk elkapni. Ezen kívül ha elkapunk "minden" hibát, akkor nagyon könnyel elrejtünk magunk elől hibákat, hisz azok nem fogják összefosatni a programot. Én nem egy esetbe kerültem ilyen hibába, és bizony igencsak nehéz úgy kijavítani a programot, hogy semmit se árul el a működéséről.
Az is igaz viszont, hogy gyorsabban történik az exception ágba az ugrás ha minden exceptiont elkapunk. Ezt a paramétersor elhagyásával tehetjük meg, így a kód így fog kinézni:
import time start = time.time() a = "314159265358979323" for z in xrange(1000000): try: int(a) except: pass print time.time()-start
Az eredmények így:
int.py: 0.878s
string.py: 1.903s
Az int.py sebességének csökkenése nem számottevő, viszont a string.py minden egyes teszt során 2mp alatt maradt. Így a string-es eset 2.1x tart tovább.
5 - Excepiton elhagyása
A fő probléma az, hogy sokáig tart mire az excepiton "megszületik", és mire ez lekezelésre kerül. Hála égnek még az egész procedúra előtt meg tudjuk állapítani hogy a string csak számokat tartalmaz-e.
A stringeknek van egy .isdigit() metódusuk, ami True logikai értékkel tér vissza ha a string minden egyes karaktere számjegy. Minden más esetben False az eredmény.
Az ilyen logikai értékeket if / else szerkezettel lehet vizsgálni. Lássuk:
import time start = time.time() a = "314159265358979323" for z in xrange(1000000): if a.isdigit(): int(a) else: pass print time.time()-start
Eredmények:
int.py: 1.003s
string.py: 0.183s
Az int.py sajnos lassabb lett, 1.14x tovább tart a futása. Ez érthető, hiszen először leellenőrizzük hogy az 'a' változó szám-e. Ha igen, akkor utána történik a típuskonverzió. Az első két esetben a try blokkba lépés nem tart semeddig, és ha szerencsénk van, és az int() nem dob exceptiont akkor gyorsan megvoltunk a típuskonverzióval.
A string.py viszont nagyon sokkal gyorsabb lett, 10.4x gyorsabban fut le a felső két megoldás.
6 - Konkluzió
Ilyen eredménnyel nem teljesen egyértelmű hogy most mit kéne alkalmazni. A felhasználók többsége helyes értéket fog beírni, tehát int-et, így gyorsabb ha az try/except ágba van téve, viszont a hibával lényegesen több időt foglalkozik a program.
A példák közül az első, az alap kód a "Pythonos" megoldás. A végeredményül kapott kód pedig inkább "C"-s, tehát bátran használja mindenki az első megoldást, hiszen Pythonba programozunk, nem C-be.
list, array, tömb
Olyan régen írtam már utoljára, hogy nem is igazán emlékszem rá hogy mit mondtam el és hogy mit nem, úgyhogy nincs kizárva, hogy elfelejtek valamit itt most tisztázni.
Na de tehát most a tömbökről lenne szó, pythonosan hívjuk csak list-nek.
A list-ek létének a "miértjét" már a python változók, típusok [party1]-be leírtam, indexelését, ilyeneket is. Jópár lényeges dolog kimaradt akkor, például hogy az adat mégis hogyan kerül majd bele egy ilyen list-be. Lássuk akkor ezeket.
0 - Ebben a fejezetben
1- Elemek hozzáadása tömbhöz
1.3 - Elem hozzáfűzése a tömb elejéhez
1.4 - Tömb mérete
2 - Tömb megtöltése igazi adattal
3 - Törlés list-ből
3.1 - remove()
3.2 - del
4 - List comprehension
5 - Zárás
1 - Elemek 'hozzáadása' egy tömbhöz.
Logikusnak tűnhet, hogy a + operátorral lehet a tömbhöz elemeket hozzárakni. Ez így teljesen nem igaz.
List-hez hozzáfűzni szokás adatot, ami angolul append. Ezzel a tömb végére kerül az adat. Többnyire ezt használjuk. A példában indítsunk egy tök üres list-el.
tomb = []
print tomb
#>>[]
tomb.append("alma")
print tomb
#>>['alma']
A példában tehát a list 0. eleme az "alma" string lett. (Nem elfelejteni: a tömb első elemének az indexe 0!)
Esetleg felmerülhet a kérdés, hogy lehet-e rögtön a 2. indexre adatot helyezni? Nem lehet. Az append() függvénybe nem is tudjuk sehova se beírni az indexet. Meg amúgy semmi értelme sincs.
1.1 - Hol van az append() függvény?
Eléggé sok minden, köztük a list is egy objektum a pythonba. Most nem akarom elmagyarázni hogy az mi pontosan. Annyit elég jelenleg tudni róla, hogy mivel a list objektum, ezért amikor egy list-et létrehozunk, akkor azon felül hogy adatokat teszünk a változóba automatikusan függvényeket is "bele rak" a python (metódusoknak szokás nevezni őket). Ezek a metódusok funkciókat valósítanak meg a változón saját magán, a benne levő adatban.
És a lényeg hogy egy objektum metódusait a következő formában lehet elérni:
Példánkban tehát a "tomb" list-be levő append metódust hívtuk meg, és az pedig hozzácsapja a list végéhez a paraméterlistán kapott adatot (esetünkbe stringet).
1.2 - Elemek hozzáadása a "+" operátorral
Nem hiába nem ezzel kezdtem. A + operátor összefűz listákat méghozzá úgy, hogy az operátor jobb oldalán levő objektumot végig iterálja, és a kapott értékeket egyesével hozzáfűzi a tömbhöz.
Lássunk inkább egy példát:
tomb = ["alma"] print tomb #>>['alma'] tomb += "medve" print tomb #>>['alma','m','e','d','v','e']
Miért is történt ez? Nem elfelejtendő, hogy a string egy felsorolt típus (majdnem mint egy tuple). A + operátor végig iterálta a stringet, így minden egyes iterációval a string egy karakterét kaptuk (az előző postban szépen részletezem ezt a a dolgot a for ciklussal). A kapott karakterek lettek hozzáfűzve a tömbhöz.
Mondjuk azt, hogy ezt a stringet mindenképp a + operátorral akarjuk a tömb végére tenni. Most már tudjuk, hogy a + operátor mindenképp végig iterálja a jobb oldalát. Tehát akkor tegyünk valami olyan típust oda, amin hogyha iterálunk a stringet kapjuk meg. Egy ilyen típus például a list.
tomb = ["alma"] tomb += ["medve"] print tomb #>>['alma','medve']
Egyszerűbben fogalmazva: tömbök összefűzésére kell használni a + operátort.
1.3 - Elem hozzáfűzése a tömb elejére
Ezt a műveletet prepend-nek szokás nevezni. Teljesen érthető a gondolat, hogy ha .append() metódus létezik, akkor .prepend()-nek is illene. Sajnos nem így van.
Az .insert() metódus megadott indexre szúr be értéket. Tehát nem írja felül, hanem "oda rakja". Ez nagyon jó, így mondhatjuk azt, hogy a 0. pozicióba kerüljenek be az új elemek.
Ahol az index tehát az amelyikre a be akarjuk szúrni az elem-et.
Egy példa
tomb = ["alma"] tomb.insert(0,"medve") print tomb #>>['medve','alma']
Megint meg lehet kérdezni, hogy lehet-e mondjuk a 20. indexre beszúrni elemet. Ha nagyobb indexet adunk az .insert()-nek mint ahány elem van a tömbben akkor a tömb legvégére kerül az elem, tehát ilyenkor úgy viselkedik mint az .append().
1.4 - Tömb mérete
Azt hogy a tömbben hány elem van a tömb hosszának vagy méretének szokták nevezni. Logikusnak tűnhet hogy a tömb méretét a .length() metódus adja meg. Viszont az előbbi .prepend() metódus hiányában már az is logikus lehet, hogy nincs .length() metódus. És nincs is.
Bármilyen felsorolt típus méretét a len() függvény mondja meg.
A len() függvény paraméteréül csakis egy értéket vár. Tehát ha két vagy több list méretét akarjuk megtudni akkor vagy külön-külön meghívjuk a len() függvényt mindegyik list-re, és az eredményeket összeadjuk, vagy először összeadjuk az összes list-et. Természetesen elég csak a függvény hívásakor összeadni őket, hisz nem azt szeretnénk alapvetően, hogy két teljesen független list összekapcsolódjon. Csak problémát jelentene.
tomb1 = ["alma"] tomb2 = ["medve"] print len(tomb1) #>>1 print len(tomb1)+len(tomb2) #>>2 print len(tomb1+tomb2) #>>2
1.5 - Tömb tömbben
Remélhetőleg senkit se riaszt vissza a gondolat. Tömb eleme akármi lehet, így értelem szerűen tömb is. Ezt amúgy nested lists-nek szokták hívni.
Vegyük az alábbi tömböt:
tomb = ["gyumolcsok",["alma","korte"]] print len(tomb) #>>2
Bár úgy tűnik hogy 3 elem van a tömbben, mégis 2-t kapunk eredményül. Ez azért van, mert az indexek számát kapjuk meg. Bár úgy néz ki, mint ha lenne 2-es index is a tomb-ben, valójából az 1-es indexen 1db list van, és ebben a listben van két elem.
Szakszóval mondjuk úgy hogy a len() nem rekurzívan mondja meg az elemek számát.
Ahhoz hogy elérjük az 1-es indexen levű tömbből a "korte" stringet először ki kell választanunk a tomb 1-es indexét, így megkapjuk a tömböt arról az indexről. Ez után rögtön kiválaszthatjuk a kapott tömbből a "korte" szót, aminek az indexe 1.
tomb = ["gyumolcsok",["alma","korte"]] print tomb[1][1] #>>korte
Ilyen egyszerű. Az ilyen tömböket ugyan úgy be lehet járni, mint ahogy egy mappastruktúrát.
Természetesen ha a tömbben levő tömbhöz akarunk elemet fűzni, akkor kiválasztjuk az indexét, és az abban levő .append() metódust hívjuk meg:
tomb = ["gyumolcsok",["alma","korte"]]
tomb[1].append("barack")
print tomb
#>>['gyumolcsok',['alma','korte','barack']]
2 - Tömb megtöltése igazi adattal
A kézzel begépelt adatok annyira nem izgalmasak sose mint amikor valaki mástól származó információt kell a saját programunknak emészthető formára átalakítani. Időszerű tehát bevonni a felhasználót is a mókába, és az ő adataival megtölteni egy tömböt.
2.1 - Gyagyi példa 1
Úgy emlékszem hogy nekem is ezt a példát adták anno amikor először találkoztam tömbbel. A lényeg:
Kérjünk be adatokat az usertől amik bekérését a 0 begépelésével lehet megállítani. Ez után írjuk ki a bekért adatokat beírási sorrendbe (aztán meg fordítva is).
Lássuk miről is lenne szó:
Fogalmunk sincs hogy mikor kapunk 0-t, tehát egy végtelen ciklusról van szó. A 0-ás jegy begépelése megállításra szolgál, tehát azt kell megoldanunk, hogy ha 0-t gépelünk be akkor álljon meg a végtelen ciklus; ha nem 0-t akkor pedig el lehet tárolni a begépelt adatot.
A végén ki kell írni a begépelt adatokat. Ezt most nem fogjuk csak úgy odabaszni hogy print tomb, hanem igényesen egy for ciklus kiírja oda- és vissza is.
tomb = []
while True:
data = raw_input("Irj be valamit: ")
if data == "0":
break
else:
tomb.append(data)
print
print "Ezeket gepelted be:"
for item in tomb:
print item
print
print "Ez pedig a forditott sorrend:"
for item in tomb[::-1]:
print item
#>>Irj be valamit: 1
#>>Irj be valamit: 2
#>>Irj be valamit: 3
#>>Irj be valamit: 0
#>>
#>>Ezeket gepelted be:
#>>1
#>>2
#>>3
#>>
#>>Ez pedig a forditott sorrend:
#>>3
#>>2
#>>1
Első körbe létre kell hozni egy tomb list-et, máskülönben nem lenne mihez .append()-olni. Ez után indul a végtelen ciklus, és egyből fel is szólítjuk a felhasználót a gépelésre. A beérkezett adat a data változóba kerül. Egy rövid kis feltétel leellenőrzi hogy a beérkezett adat "0"-e.
Nagyon fontos hogy "0"-t ellenőrzünk, tehát stringet. Remélem nem felejtette el senki, hogy a raw_input() mindig stringet ad vissza! Ebben a példában azt se tehetjük, hogy úgy ellenőrizzük a beérkezett adatot hogy:
if int(data) == 0
Mivel ha szöveget írunk be azt nem lehet int-é alakítani (legelső leírásokban ezt magyaráztam). Ilyen hiba esetén egy exception történik, amivel teljesen máshogy kell foglalkozni. Az if-el nem lehet az ilyen hibákat kezelni.
Ha a szöveg nem "0" akkor hozzáfűzzük a tomb-höz. Tehát így már megy frankón a ciklus.
Ha a ciklus egyszer véget ért, akkor a kód egy üres print utasításra ugrik. Ennek hatására egyszerű üres sor jelenik meg a képernyőn... csak a jobb olvashatóság érdekében. Ez után kiírjuk az usernek hogy most mit fog látni: az elemeket a begépelés sorrendjében. Alapvetően egymás után kerülnek hozzáfűzésre az adatok a tömbhöz, így tehát a tömb elejétől a vége felé kell haladnunk. A for ciklussal szépen végig iterálunk a tomb-on. Az aktuális eleme a tomb-nek az item változóba kerül, ezt pedig a cikluson bellül kiíratjuk.
Ez után megint felszólítás következik, majd a kiírás. Tömböt megfordítani lehet több eljárással is, mi viszont csak a kiírás pillanatában akarjuk fordítva látni.
Az indexelésről szóló bejegyzés végén írtam erről a pofon egyszerű list megfordító technikáról. A [::-1] hatására a tomb fordítottját kapjuk vissza, amin a for ciklus végig is iterál.
Ennyi lenne az egész. Igazán könnyű szerintem.
2.2 Gagyi példa 2
Mondjuk azt hogy emberek neveit és életkorait akarjuk eltárolni. Ha ez megvan akkor utána kérdezzük meg a felhasználótól hogy a hány évnél fiatalabbakat akarjuk kilistázni. 0-val lehessen megszakítani a bekérést.
Igazán hasonló a feladat az előzőhöz, csak a bekérésnél több mindent kell bekérni, és a kiíratásnál kell egy feltétel.
emberek = []
while True:
nev = raw_input("Nev: ")
if nev == "0":
break
else:
kor = int(raw_input("Eletkor: "))
emberek.append([nev,kor])
maxKor = int(raw_input("Hany evnel fiatalabbakat mutassam? "))
for item in emberek:
if item[1] < maxKor:
print "Nev: %s, kor: %s" % (item[0],item[1])
#>>Nev: Adam
#>>Eletkor: 16
#>>Nev: Anna
#>>Eletkor: 18
#>>Nev: Sandor
#>>Eletkor: 22
#>>Nev: 0
#>>Hany evnel fiatalabbakat mutassam? 19
#>>Nev: Adam, kor: 16
#>>Nev: Anna, kor: 18
A ciklus elsőnek a nevet ellenőrzi le. Ha itt már "0"-t kapunk akkor be lehet fejezni az egész ciklust. Ha nem "0" akkor bekérhetjük az életkort. Ha ez itt nem felismerhető szám, akkor elhasal a program.
Az emberek list-hez hozzá fűzűnk egy list-et, aminek elemeit a nev és a kor változók értékei teszik ki. Ha kiíratnánk az adatok felvétele után az emberek változó értékét, akkor ezt kapnánk:
#>>[['Adam','16'],['Anna','18'],['Sandor','22']]
Azaz a 3 elemű tömb minden egyes elemén egy két elemű tömb található.
A ciklus után bekérésre kerül az életkor, ezt egyből int-é is alakítjuk. Ezek után végig haladunk a bekért adatokon. Tudjuk hogy az 1-es indexen szerepel a bekért életkor, tehát erre végezzük a feltétel vizsgálatot. Ha teljesül a feltétel, akkor kiírjuk a változók tartalmát.
Az előző bejegyzésben szó volt a list-ek kicsomagolásáról, a list unpack-ról. Ez a példa tökéletesen szemléltei a list unpack értelmét, ezért át is írnám a kódban a:
for item in emberek: if item[1] < maxKor: print "Nev %s, kor: %s" % (item[0],item[1])
Sort a következőre:
for nev, kor in emberek: if kor < maxKor: print "Nev: %s, kor: %s" % (nev, kor)
Az ilyen kódot öröm elolvasni.
3 - Törlés listából
Az előző példa jó, mert kiírjuk a bekért adat egy feltételnek megfelelő részét, az eredeti adat mégis megmarad. De mondjuk azt, hogy szeretnénk ha folyamatosan egyre kevesebb adat maradna. Ez teljesen hétköznapi probléma.
A legjobban egy tömbnyi számmal látszódik ez a példa. Mondjuk azt hogy egy list-ben benne van 10 tök véletlen szám. A programunk futása végén csak a párosak maradjanak benne.
A páros/páratlan számok kiválasztása igen csak alapvető művelet. Az adott számnak a maradékát kell képezni 2-vel. Ha 0 a maradék, a szám osztható 2-vel. Ha 1 a maradék, akkor pedig nem. Ezek után már minden egyszerű:
szamok = [1,6,8,0,7,3,9,4,5,2] print szamok for szam in szamok: if szam%2 == 1: szamok.remove(szam) print szamok #>>[1, 6, 8, 0, 7, 3, 9, 4, 5, 2] #>>[6, 8, 0, 3, 4, 2]
Egészen jónak tűnik a 3-as számig bezárólag. Nem tűnik annyira nagy hibának, viszont egy átkozottul fontos és észben tartandó probléma miatt maradt ott.
3.1 .remove()
A .remove() metódus kitörli a lista első olyan elemét amit a paramétersoron adunk neki. Kétszer is mondtam már, de most harmadszor is: csak egymást követő indexek lehetnek a list-ben. Tehát a .remove()-al az egész list elcsúszik egyel balra.
A for ciklus az iterálás során folyamatosan növel egy számlálót, ami tárolja hogy hányadik iteráció zajlott le. Ez képezi a tömb esetén az indexet. Mivel a tömb egy adott elemének a törlése esetén a tömb minden eleme egyel balrább csúszhat így a kitörölt elem után közvetlenül következő elemet a ciklus átugorja!
Nézzük a fenti példát futás közben:
Azaz valójából a piros számok kerülnek vizsgálatra. Az első esetben, a 0. indexen az 1-es számjegy el is tűnik, hisz az páratlan. Elcsúszik minden balra, és utána az 1. indexen levő érték kerül vizsgálatra.
Nem is kell végig magyaráznom, hogy gyakorlatilag minden páratlan szám utáni számjegy egyszerűen átugrásra kerül. Tehát a 6-os szám nem is azért jelenik meg a 0. indexen mert az nem páratlan, hanem mert a list elcsúszása miatt kimaradt a vizsgálatból. Bizonyítékképp írd át a 6-os számot valami páratlanra. Lehetőleg 10-nél nagyobbra, nehogy bekeverjen még az is a képbe, hogy a .remove() az első találatot törli. Ha átírod a 6-os számjegyet mondjuk 11-re, akkor 11 fog a lista 0. indexén lenni.
3.2 Törlés list-ből index alapján
A .remove() tehát a paraméteréül kapott értéket törli ki a tömbből. Ha egy adott indexen levő értéket akarunk megsemmisíteni akkor a del utasításra van szükség.
Ugyan úgy lehet használni mint a print-et, tehát zárójelek nélkül. Az indexelés nem szorul most már magyarázatra úgy hiszem. Természetesen itt is elcsúszkál a tömb, ez várható volt. Annyi jó viszont van ebben a kifejezésben, hogy mivel indexet kell megadni, ezért ott nyugodtan szeletelgethetjük a változónkat.
tomb = range(1,10) print tomb del tomb[0] print tomb del tomb[5:] print tomb #>>[1, 2, 3, 4, 5, 6, 7, 8, 9] #>>[2, 3, 4, 5, 6, 7, 8, 9] #>>[2, 3, 4, 5, 6]
Ez így szép és jó, viszont még mindig nem sikerült abból a 10 nyamvadt számból kiszülni a párosakat. Megmutatom erre az igazi pythonos megoldást.
4. List comprehension
Ez a technika becsületes neve, és komolyan mondom gyönyörűen elegáns, és nagyon egyszerű. Sajnos nem tudom mi a magyar fordítása ennek a kifejezésnek, ezért így a saját nevén fogom szólítani.
List comprehension nem minden nyelvben van, de örüljünk hogy itt igen.
A lényege a technikának az lenne, hogy egy list-ből létrehozunk egy másikat egy paranccsal. Az ilyen kódot több sorba is meg lehet írni, és igazából ez a megoldás a "sok soros" változat leírva egy sorba. Na de inkább egy példa:
tomb = [1,6,8,0,7,3,9,4,5,2] print tomb parosak = [szam for szam in tomb if szam%2==0] print parosak
Na igen, erről lenne szó. Elsőre talán kicsit bizar a leírt utasítás, de nagyon könnyen megérthető.
4.1 A list comprehension megértése
Ha balról-jobbra olvassa olyan aki még sose látott ilyet, akkor nem biztos hogy elsőre leesik hogy mi történik, ezért inkább ajánlom középről kezdeni, a for parancstól.
for szam in tomb #Ez remélem érthető. A tomb-ből kiveszünk egy értéket, a szam-ba kerül. if szam%2==0 #Ez a feltétel az előbb kivett szam-on dolgozik. Ha teljesül a feltétel, akkor a kifejezés sor elejére ugrunk: szam #Ez az egyszerű kifejezés nagyjából ezt takarja: #.append(szam) #Ahol az .append() bal oldalán a parosak változó áll, valahogy így: #parosak.append(szam)
Tehát a for ciklus végig ugrál a tömbön, és amelyik szám megfelel a feltételnek az hozzáfűzésre kerül az = jel bal oldalán található tömbhöz. Ilyen egyszerű.
A fenti kód leírható több sorban is. Egyetlen különbség hogy előre létre kell hozni egy parosak tömböt:
tomb = [1,6,8,0,7,3,9,4,5,2] parosak = [] for szam in tomb: if szam%2 == 0: parosak.append(szam) print parosak #>>[6, 8, 0, 4, 2]
Ugyan úgy működik, de hát azért ez egy sorban sokkal elegánsabb.
A list comprehension-nek van egy nagyon jó tulajdonsága, ami igencsak megkönnyíti a programozó életét: az eredményül kapott tömb vissza adható értékül annak a tömbnek amin lefuttattuk. Kóddal egyszerűbb:
tomb = [1,6,8,0,7,3,9,4,5,2] tomb = [szam for szam in tomb if szam%2==0] print tomb
Bizony, ez így már nagyon rövid, és nagyon elegáns.
Ki lehet nyugodtan próbálni hogy a hosszabb kódban nem a parosak-hoz fűzzük hozzá a szam-ot, hanem a tomb-hoz. 2 dolog fog történni:
1- A tomb elején ott lesznek a számok, hiszen senki se szedte ki belőle a páratlanokat
2- Viszont mivel a list mérete folyamatosan nőni fog a .append()-ek miatt így a végtelenségig nő a listánk ami megeszi az összes ramot amit a hűtőben talál.
4.1.1 Kötelező bele a feltétel?
Kicsit defektesen vezettem be ezt a fejezetet, ezért így a végére leírnám: nem kötelező feltételt tenni a list comprehension végére.
4.1.2 List comprehension zárás
Ahogy mondtam: a kifejezés elején álló szam olyan mintha azt mondanánk: .append(szam). Az .append()-et meg már igencsak jól ismerjük, így hát cifrázzuk kicsit a list comprehension-ünket: a tomb minden elemét emeljük négyzetre:
tomb = [1,6,8,0,7,3,9,4,5,2] tomb = [szam**2 for szam in tomb] print tomb #>>[1, 36, 64, 0, 49, 9, 81, 16, 25, 4]
Remélem senki se felejtette el, hogy hatványozni a ** operátorral lehet.
5 - Zárás
Csak két hónapot kellett várni erre a bejegyzésre, de szerintem egész sok minden kitárgyalásra került a list-ekről kapcsolatban. A következő bejegyzésben a dict-ekről lesz szó.
python ciklusok 2 - a for ciklus
Meglehetősen régen írtam bejegyzést utoljára, és ég is a pofámról a bőr.. de hát nincs nagyon mit tenni, a slapec.tk se igazán frissül.
Kicsit zavaró amúgy, hogy a kódjaim idővel "szétesnek", ugyanis a freeblog és én máshogy értelmezzük a sortörést, ami nem is lenne gond, csak a SyntaxHighlighter -ami kiszínezi a kódjaimat- a <br> tagot is kiemeli, hisz az is kód.
Ezektől eltekintve eljött az idő hogy kicsit jobban belemásszunk a for ciklus lelki világába. Nem kell félni, nem fog megharagudni érte ,)
0 - Ebben a fejezetben
1- Lista elemeinek olvasása
1.1 Lista elemeinek kiírása
1.1.1 while
1.1.2 for
1.1.3 Mi is az az iterálás?
1.2 Számozott kiírás
1.2.1 Lista kicsomagolása (unpack)
1.3 range()
2. Haszontalan példa
1 - Lista elemeinek olvasása
Ennek a témának a jobb megértéséhez lehet hogy nem ártana a list-ek átható ismerete, de most megpróbálom azok mérsékelt ismeretével elmagyarázni a dolgokat. A listákról volt pár rövid gondolat a "python operátorok, típusok [part 2]" -ben, mint hogy hogyan lehet létrehozni/észrevenni őket, és a slice-ok, tehát hogyan lehet indexelni benne egy elemet, többet, vagy kivágni darabkákat belőle.
Azért vannak viszonylag szoros kapcsolatba a ciklusok a listákkal (list-ekkel, tömbökkel, kinek mi tetszik) mert hogy ezek a típusok felsorolt típusok, így hogy végig tudjunk haladni rajtuk ciklikusan ismétlődő algoritmusokra van szükségünk, így tehát ciklusokra; pythonba for-ra és while-ra.
1.1 - List elemeinek kiírása
Ugye a Python beépített print függvényéről ismeretes hogy igyekszik mindenféle adatot a képernyőn láthatóvá tenni. Ha a print-be egy natúr list kerül, úgy kód szerűen látni fogjuk az összes elemet. Így:
array = ["kis szoveg",2011,3.1415] print array #>>['kis szoveg', 2011, 3.1415]
Ez a kimenet egy programozónak tökéletes, de egy halandó ember inkább számít a felsorolt adatok rendezett kimenetére. A feladat tehát adott: írjuk ki a tömb elemeit egymás alá.
1.1.1 - while
array = ["kis szoveg",2011,3.1415] counter = 0 while counter != len(array): print array[counter] counter += 1 #>>kis szoveg #>>2011 #>>3.1415
Akik már programoztak más nyelvekben, azoknak ez a megoldás ismerős is lehet. Valahogy meg kell tudni hogy hány elem van a tömbben, kell egy ciklusváltozó ami számolja hogy hanyadik indexnél járunk (az ezen az indexen levő tömb elemet is írjuk ki), és a számlálót növelgetni kell.
A len() függvény visszaadja bármilyen felsorolt típusból hogy hány elemet tartalmaz.
Ez a megoldás úgymond "C-s". Bár igen logikus a működése, pythonba nem így szokás.
1.1.2 for
array = ["kis szoveg",2011,3.1415] for item in array: print item #>>kis szoveg #>>2011 #>>3.1415
Ez azonnal csinosabbnak néz ki. Nézzük hogy mi is történik valójábant:
A for ciklus végig iterál az array list-en. A lista aktuális eleme bekerül az item változóba. A ciklusmagon belül pedig kiírjuk az item értékét. A ciklus újbóli futásával az item-be a lista következő eleme kerül. A ciklus addig fut ameddig vannak elemek.
Azzal hogy for ciklus választottunk egy csomó kódtól kíméltük meg magunkat. Először is nem kell számolunk hogy hogy hányadik elemnél járunk. Mivel nincs mit számolni, így növelni se kell semmit. A tömb méretét se kellett képeznünk, és még feltételt se kellett írni.
1.1.3 Mi is az az iterálás?
Az iterálás egy eléggé gyakori és fontos része a pythonnak. Pythonban az iterálásban a for segít.
Iterálni alapvetően 3 dolgon lehet:
A lista aktuális eleme belekerül a segédváltozóba; ahogy a fenti példán volt.
Objektumon
Az objektumoknak lehetnek terátor metódusai (__iter__). Ha egy ilyen jelen van az objektumban, akkor a for ciklus ezt fogja meghívni, és a metódus aktuális visszatérési értéke a segédváltozóba kerül. Természetesen ha egy objektum listával tér vissza, akkor az 1-es pont érvényes rá.
Generátor függvényen
Pythonba léteznek generátor függvények, amik gyakorlatilag objektumokká változnak -nyelvi szinten-. Ezek minden hívással más értékkel térhetnek vissza, úgy ahogy a 2. pontba leírtam.
Az iterálás lényege tehát hogy egy ciklus -ami esetünkben a for- végignyálaz valamilyen homogén objektumot -listát, objektumot, generátort- és az egyes hívásokkal kapott értékeket a segédváltozóba rakja.
1.2 Számozott kiírás
Mondjuk azt, hogy a kiírt elemek elé sorszámot akarunk tenni. A fenti két példa közül a while esetében a ciklusszámláló (counter) kéznél van, ezt berakjuk a print-be, hozzá adunk egyet (csak hogy 1-es legyen a felsorolás első sorszáma) és már készen is vagyunk.
Ha ugyan ezt for ciklussal akarnánk megcsinálni, akkor a while-os példából kiindulva egy számlálót be kell iktatnunk a kódba, így:
array = ["kis szoveg",2011,3.1415] counter = 1 for item in array: print "%s. %s" % (counter,item) counter += 1 #>>1. kis szoveg #>>2. 2011 #>>3. 3.1415
Természetesen ezt nem írtam volna le, ha nem lenne rá más megoldás.
Ha a ciklusváltozó értékére lenne szükségünk, akkor a list-et az enumerate() függvénybe kell helyezni.
array = ["kis szoveg",2011,3.1415] for counter, item in enumerate(array): print "%s. %s" % (counter,item) #>>1. kis szoveg #>>2. 2011 #>>3. 3.1415
1.2.1 List kicsomagolása (unpack)
Remélem azonnal feltűnt, hogy 2 ciklusváltozónk is lett, a counter és az item. Ez úgy lehetséges, hogy a for ciklus képes egy adott indexen levő belső tömb elemeit több változóba kirakni. Ezt unpack-nak hívják (kicsomagolás, eléggé takarja a valóságot az elnevezés). Egy példával sokkal egyszerűbb lesz:
array = [[1,"elso elem"],[2,"masodik elem"]] for index, item in array: print index, item #>>1 elso elem #>>2 masodik elem
Azaz a következő történt:
A for ciklus kivette az array 0. elemét, ami egy két elemű tömb. A tömb 0. eleme az index változóba, az 1. eleme az item változóba került.
Nem kötelező így kibontani a tömbjeinket. Általánosabb is, hogy csak a belső tömböt szedik ki a ciklussal, és a ciklusmagban pedig indexelik az elemeit (vagy egy változónak értékül adják). Mindkettőre egy-egy példa:
#Elobbi pelda array = [[1,"elso elem"],[2,"masodik elem"]] for item in array: print item[0], item[1] #Utobbi pelda for item in array: actual = item[0] item = item[1] print actual, item
Mindkettő ugyan azt a kimenetet eredményezi.
Egy apróságot megjegyeznék:
A 2. példakódban az item-nek értékül adtam az item[1]-et. Ezt meg lehet csinálni, de csak ésszel. Mint látható, az actual változó értéke az item[0]. Ha előbb az item = item[1] kifejezés futott volna le, akkor az item változóba már nem 2 érték lenne, hanem már csak egy (hisz felülírtuk a kéttagú énjét az egytagúval). Biztonságosabb egy 3. változónevet bevezetni.
A példából remélem nyilvánvaló lett: az enumerate() függvény a példában szereplő tömbhöz nagyon hasonló "valamit" ad vissza, ezért működik.
1.3 range()
A range() függvény szerepe világos kell hogy legyen, az ezt megelőző leírásban ezt tárgyaltam.
Most már mindenkinek nyilván való hogy az for az egy iterátor, magától nem számol semerre. Nem is képes csak felsorolt típusokon (vagy más iterálhatóakon) végig haladni. Akkor a range() függvénynek minden bizonnyal egy listát kell vissza adnia, vagy iterátornak kell lennie. Nézzük meg:
print range(5) #>>[0, 1, 2, 3, 4]
Igen, bizony. A range függvény egy list-et ad vissza, amiben azok a számok szerepelnek amiket előállított nekünk. Ezen a listán halad végig a for ciklus. Ezért működik.
2. Haszontalan példa
Amikor még igen csak kezdő voltam, nem is gondoltam, hogy a for ciklus így működik. A Pascal-os és AHK-s beidegződéseim miatt csak számokat generáltattam a for-al, és a ciklusmagon bellül indexelgettem a tömb elemeit. Érthető, minden más nyelvben így kell. A példának okáért megmutatnám hogy hogyan is:
array = ["szoveg",1337,3.1415] for counter in range(0,len(array)): print array[counter]
Gyakorlatilag meg van minden itt is, mint a legelső while-os példában.
Ha netán magadtól ilyen, vagy ehhez hasonló tömb bejárásokat írtál, akkor semmi gáz: más is hasonlóan kezdte.
Azért hangsúlyoznám: ne így csináljátok ahogy ebben a példában leírtam! A Python nagyon ügyesen kezeli a list-eket, látható volt: lehelletnyi kóddal mindent megold.
3. Végszó
Ez egy egészen tűrhető hosszúságú bejegyzés lett. A következő bejegyzésben a list-ről lesz szó.
python ciklusok (for, while, do..while)
Nem is lehetne igazi slapec(tm) blog a pythonblog sem, ha nem tűnnék el a semmibe 1-2 hónapra. Igazán sajnálom, pyGTK-t kezdtem el megtanulni és egy saját twitter kliens fejlesztésébe kezdtem, illetve az egyetem is megy, szóval sok más dolgom van. Na, de akkor lássuk a ciklusokat!
0 - Ebben a fejezetben
1 - Mik azok a ciklusok?
2.2 - A for ciklus
2.4 - A while ciklus
2.5.3 - Do..while
2.6 - Végtelen ciklus
2.7 - "Gondoltam egy számra"
1 - Mik azok a ciklusok?
A ciklus valamilyen periodikusan ismétlődő folyamatot jelent. Esetünkben a folyamat egy kód akar lenni, amit szeretnénk ha újra és újra ismétlődne. Az ismétlődés végét egy feltétel fogja jelezni, innen tudja a Python, hogy vége az ismételgetésnek.
A legtöbb ciklus valamikor véget ér, ez az egészséges reakció. Persze léteznek végtelen ciklusok is, amik addig futnak ameddig csak tudnak (valójából ezekből is van kiút, ha más nem, hát a folyamat leállítása).
Ciklusokra akkor van szükségünk, amikor egy konkrét kódot többször akarunk hogy lefusson, netán úgy, hogy egyes a ciklusban levő értékek még meg is változnak útközben. Könnyű rájönni hogy mikor van szükség ciklusra: ha van egy konkrét mennyiségű kódunk, és azt le akarjuk másolni és pontosan ugyan úgy beilleszteni rögtön a vágólapra helyezett kód után, akkor ott ciklusra lett volna szükség. Kezdjünk is el egyből gondolkodni rajta.
2 - Ciklusok
Kétféle ciklus létezik (értsd: ismerek én/tudok róla), az elöltesztelős és a hátultesztelős. A különbség köztük, hogy az elöltesztelős a ciklus feltételét leellenőrzi még mielőtt a ciklusmag egyszer is lefutott volna. A hátultesztelős ciklus ciklusmagja ezzel szemben egyszer lefut, még mielőtt a feltétel kiértékelésre kerülne. Mindkettőnek megvan a maga értelme, hozok majd rájuk példákat.
2.1 - Ciklusok felépítése
Pythonban a ciklus a ciklus fejlécével kezdődik, amiben a ciklus feltétele található. Ez alatt új bekezdése (indent level ugye) következik a ciklusmag.
Fontos: A ciklusmag addig ismétlődik, ameddig a feltétel igaz!
2.2 A for ciklus
Minden nyelvben fellelhető, a legtöbbet használt ciklus a for ciklus. Ez a ciklus igen egyszerű dolgot csinál. Megadott számszor lefuttatja a magjában található kódot. Tehát pontosan azt csinálja, amire számítottunk idáig.
Vegyünk is egyből egy tök egyszerű feladatot. Írassuk ki a számokat 0-tól 10-ig. Ciklusok nélkül ez ugye 10db print, blabla, vagy egy jó hosszú string kiírása lenne ("1\n2\n3\n4", értitek). Ha nem lenne eléggé meggyőző az érv: kérjük be a felhasználótól hogy meddig írja ki a programunk a számokat. Nos igen, ezt nem tudjuk előre.
Először az első példa:
for a in range(0,10): print a #>>0 #>>1 #.. #>>9
2.2.1 A for ciklus felépítése
Elsőre érthetetlen dolgok történnek, ideje megérteni őket.
for CIKLUSVÁLTOZÓ in range( METTŐL , MEDDIG , HANYASÁVAL ): ciklusmag
Ez a teljes for ciklus. A példámban közel nem volt ennyi érték a range-be (ami egy függvény mellesleg). Mivel a kódom lefut, gyanítható, hogy egyes értékek elhagyhatóak. Na elég a rizsából, nézzük mi-mit csinál.
A for -al jelezzük hogy ciklusról lesz szó. A ciklusváltozót ugorjuk egy pillanatra át, menjünk a kifejezés végére. A range függvény létrehoz egy listát, aminek első tagja a METTŐL lesz, de már a MEDDIG nem kerül bele. Az értékek egyesével fognak növekedni. A HANYASÁVAL elhagyható, de azért azt jegyezzétek meg: ez csak egész (int) szám lehet! A for ciklus elindul a range listáján, és az éppen aktuális érték a CIKLUSVÁLTOZÓ-ba kerül bele. Ezt amúgy az in kulcsszó intézi el nekünk.
Még mielőtt arra gondolna valaki, hogy a MEDDIG azt jelenti, hogy a METTŐL-től hány számot kell létrehozni jelezném hogy téves a gondolat, ugyanis ha például kettesével ugrálunk, akkor nem kétszer több eredményt kapunk (hanem fele annyit).
2.3 for példák
2.3.1 Visszaszámlálás
Bátran szabad a range() paramétereit próbálgatni, semmi bajunk nem lehet belőle :)
Nem is szeretnék ezzel időt húzni, egy példát emelnék ki, ez pedig a visszafelé számoló ciklus. Logikusnak tűnik, hogy a range(10,0) 10-től visszafelé számol, ez viszont nem igaz. Ilyenkor kötelező lépésszámot adni, ami egyszerűen -1, azaz 10-től -1-esével haladunk előre, gyakorlatilag a megadott számhoz fogunk eljutni.
for a in range(10,0,-1): print a #>>10 #>>9 #.. #>>1
2.3.2 Páros számok kiíratása
Elsőre logikusnak tűnhet hogy kettesével lépkedő ciklus csinálunk, viszont ez csak akkor működik, ha páros számról indulunk. A klasszikus példa a következőképp néz ki:
for a in range(1,20):
if a%2 == 0:
print a
Azaz a ciklus 1..19 között fog végig haladni. Ugye oszthatóságot maradékképzéssel vizsgálunk (most) (Ha egy számban egy másik szám maradék nélkül van meg, akkor azok ugye egymás többszörösei, nem kell magyaráznom remélem). Tehát a ciklusváltozó (a) aktuális értékének képezzük a maradékát 2-vel, és ha az eredmény 0, akkor ki is írjuk a ciklusváltozót. Else ágra nincs szükség, mert mi csak kiíratni akarjuk a feltételnek megfelelő számokat, és kész vége, ennyi.
2.3.3 Összes osztó
Ez egy igazán alap feladat, ezért illik megcsinálni. A feladat tehát: kérjünk be egy számot, és írjuk ki az összes osztóját.
A kód igen egyszerű:
szam = int(raw_input("Irj be egy szamot: "))
for a in range(1,szam):
if szam%a==0:
print a
A logika igen egyszerű: számoljunk el a beírt számít, nézzük meg hogy mely számokkal való osztás maradéka 0, és azok osztói a beírt számnak, így ki is írjuk. Elszámolni a ciklusváltozó fog ugye, az osztást egy if végzi, az értékes információt pedig a print kiírja. Ezek a számok azok lesznek, amik maradék nélkül megvannak a begépelt számban.
Ennél lehet gyorsabbat is csinálni, például ha csak a feléig számolunk el. Ez szimplán: range(1,szam/2+1)
Fontos: hozzá kell adni még 1-et az eredményhez, mert -mint írtam- az a szám, ameddig el kell számolni már nem kerül bele a ciklusba, így minden páros szám közepe kimarad, ami ciki.
Elég mellesleg a szám gyökét venni, így még kevesebb felesleges számolgatás van, viszont azt már nem írom le (nem mintha nehéz lenne).
2.4 A while ciklus
A while is egy elöltesztelős ciklus mint a for, viszont ez valódi számokkal dolgozik. A for ciklusban később jobban el fogok mélyülni, ugyanis ott nem minden az, aminek látszik. Egyelőre elég tudni róla, hogy adott számtól adott számig számol.
Tehát a while ciklus magja addig ismétlődik, ameddig a feltétel igaz. Könnyű a feltételt magyar hétköznapi mondatba is megfogalmazni, például: "Amíg szam kissebb mint tíz, kiír szam". Akkor írassuk is ki a számokat 10-ig a while-al is!
a = 0 while a!=10: print a a+=1
2.4.1 A while ciklus felépítése
while FELTÉTEL_IGAZ: ciklusmag
Egyszerűbb mint a for ciklus, és itt bizony bármilyen logikai feltétel megetethető a ciklussal. A fenti példán látható, hogy a while-al kicsit több kódba kerül egy szám növelgetése 1-el. Gyakorlatilag az amit a for ciklus 1 sorba megcsinál, az itt 3 sor, de az, hogy 3 sort kell leírnunk egyáltalán nem jelenti, hogy a kód lassabb lenne!
Először egy kezdeti értéket kell állítanunk egy változónak. Ez az a lesz, továbbiakban ez a ciklusváltozó. A feltétel: a!=10, azaz "amíg a nem egyenlő tíz". Pofon egyszerű. Kiírjuk az a-t és utána növeljük az értékét 1-el.
Bizony, ha akarnánk 0.1-el is növelhetnénk, semmi akadálya.
2.5 while példák
2.5.1 0.1-el lépő ciklus
Akkor csináljunk is egy 0.1-el növekedő ciklust.
a = 0. while a<=1.: print a a+=0.1
Fontos: A feltételbe ha a!=1-et írunk, akkor végtelen ciklust kapunk. Ennek oka a tört számok kerekítése. Az 1.0 értéke 1.0 a memóriában, viszont a 0.1-é: 0.1000000000000000055511151231257827021181583404541015625
Akárhogy adjuk ezt a számot össze, sose lesz 1.0, ezért nem lehet nem egyenlőséget vizsgálni, így kisebb-egyenlőséget vizsgálunk.
2.5.2 Bekérés helyes válaszig
Írjunk egy olyan programot, ami addig tesz fel egy kérdést a felhasználónak, amíg helyes választ nem kap a program rá.
result = int(raw_input("5+5="))
while result != 10:
print"Hibas valasz, probaljd ujra!"
result = int(raw_input("5+5="))
print "Eltalaltad!"
2.5.3 do.. while (hátultesztelős ciklus)
A hátultesztelős ciklus lényege (nem emlékszem már hogy leírtam-e feljebb, szóval akkor újra) hogy a ciklus magja egyszer lefut, még mielőtt kiértékelődne a ciklus feltétele. Pythonban viszont nem létezik konkrétan do..while ciklus, de elő lehet idézni egy kerülőmódszerrel, ami a következő:
while 1==1:
if FELTÉTEL_IGAZ
break
A következő pontban olvasható a működése:
2.6 Végtelen ciklus
Végtelen ciklust pofon egyszerű létrehozni. A legtöbb rossz ciklus alapból ilyen, kikényszeríteni pedig úgy lehet, hogy olyan feltételt adunk, ami mindig igaz. Az 1==1 például ilyen. Kicsit absztraktabb a "while 1:" feltétel. Ugye a feltételeknél volt egy olyan definíció, hogy minden érték logikai értéke True, kivétel a 0-nak. Mivel az 1 az nem nulla, így ez egy mindig igaz feltétel lesz, tehát végtelen. Ha úgy tetszik "while True:"-t is lehet írni.
A ciklus legvégére jön az a feltétel, amire ki kell lépni a ciklusból. Ebbe jobb eszközök híján egy if-el kerülünk bele. Fontos: azt a feltételt kell ide írni, aminek a teljesülésére ki akarunk lépni a ciklusból. Azaz az ilyen ciklus addig fut, ameddig a kilépési feltétel hamis!
A feltételbe kerülhet kód, gratuláció, miegymás, viszont a végére a break parancs kerüljön, ami kiugrik a ciklusból. Ezzel elértük, hogy a ciklus magja egyszer lefusson, és a futása végén kerül kiértékelésre a feltétel, és majd itt válik el, hogy lefut-e még egyszer a ciklus vagy nem.
Ezek szellemében az előző 2.5.2-es példa:
while 1:
result = int(raw_input("5+5="))
if result != 10:
print "Hibas valasz, probald ujra!"
else:
print "Eltalaltad!"
break
2.6.1 Vezérlések
Sokan nem nézik jó szemmel a break utasítás kiadását. Azt mondják hogy átláthatatlan lesz a kód, nehéz lesz értelmezni, illetve hogy mindig van rá más alternatíva. Ami az igazat illeti, én először "komolyabban" az AutoHotkey nevű makró nyelvben kezdtem programozni, ami csak 1 ciklust ismer, a Loop-ot. A kilépést mindig break-el kellett elintézni kézzel, ezért én igen megszoktam a break használatát.
Na de a lényeg, hogy a break egy olyan parancs, ami hatására az aktuális ciklusból azonnal kilépünk. Ha két ciklus van egymásba ágyazva, és a break a beágyazott ciklusban van, akkor értelemszerűen a beágyazottból ugrunk csak ki!
A break párja a pass, más nyelvekben continue. A pass nem csinál semmit, kb azt jelenti "menj tovább". A pass is egy olyan utasítás, amit átlagos programba nem szokás rakni, de persze ettől még igen fontos szerepe van: például ha kivételt kezel le a programunk, és azt akarjuk, hogy hiba esetén semmit se csináljon -> akkor csináljon pass-t, azaz menjen tovább. Majd mesélek erről is idővel.
2.7 "Gondoltam egy számra"
Végezetül egy igen egyszerű számkitalálós programot/játékot írnék le. A feladat kiírás legyen a következő: A program gondoljon egy véletlen számra, amit a felhasználónak ki kell találnia. Ha a szám nagyobb mint a begépelt, akkor azt jelezzük valahogy, ha kisebb, azt is.
import random
kitalalando = random.randint(1,20)
e = int(raw_input("Gondoltam egy szamra 1 es 20 kozt. Talald ki! "))
while e!=kitalalando:
if kitalalando>e:
print "Nagyobb"
else:
print "Kisebb"
e = int(raw_input("Probalj masikat: "))
print "Ugyes, kitalaltad"
A random libről még nem volt szó, most nem is mesélném el. A program igen egyszerűen működik:
A kitalalando változó értéke egy random integer fog lenni (random.randint) 1-20 közötti számmal. Ezután érkezik az utasítás, hogy mégis mit csinál a program, beérkezik az első szám is. A ciklus addig fog majd futni, ameddig helyes választ nem adunk (e != kitalalando). Ha a gondolt szám nagyobb, akkor az kerül kiíratásra, ha kisebb akkor pedig az. A kiírás végén újra számbekérés történik.
Mivel a while egy elöltesztelős ciklus, így ha a következő szám a helyes szám, akkor az egész while blokk futása kimarad, és azonnal az utolsó utasításra ugrunk, azaz a print-re. Mivel a ciklusban a kód addig kezdődik előröl ameddig helyes választ nem adunk, ezért csak helyes válasz esetén jelenik meg a gratuláció. Erről többet nem is nagyon lehet magyarázni :)
3 - Zárás
Ebben a részben a ciklusok alapjairól esett szó. A következő rész is a ciklusokról szól majd, illetve a for ciklus valódi értelmezéséről, tehát az iterációról, hogy saját nevén nevezzem.
Elágazások, programozási stílus
Egy kicsit könnyedebb témával indítanék, ez az lenne, hogy Pythonban mégis milyen programozási stílust érdemes követni. Utána ráállunk az IF-re.
0 - Ebben a fejezetben
1.1 A Python-os programozási stílus (indent block)
2.1 - Mi a túró a feltétel magja?
2.2 - Az IF
2.3 - Az ELSE
2.4 - Az else if (ELIF)
2.6 - IF példa (oszthatóság)
2.7 - IF példa (bekért adatra)
2.8 - IF extrák pythonban (6 > x > 2)
2.9 - És mi a helyzet a SWITCH-el?
1 - Általános programozási stílus
Általában elmondható, hogy a programozás nyelve az angol. A Pythonnak alapból problémái is vannak az ékezetes karakterekkel. Az ékezetes karakterek ráadásul speciális karaktereknek számítanak, így változónevek se lehetnek.
Használjunk tehát főként kifejező változóneveket, és lehetőleg angolt. Azt tartsuk észbe, hogy nehogy felüldefiniáljunk a kódunkban egy változót véletlenül (azaz a program során kétszer adunk neki értéket, de tudtunkon kívül). Ez ilyenkor csak szemantikai hiba lesz, utána aztán kereshetjük hogy hol hibázik a program.
Ameddig kis egyszerű programokat írunk, addig ha egy változóban számok vannak, azt hívjuk numbers, num, nums, v ilyennek. Az állapotokat tároló változók elnevezésekor nyugodtan használjuk a változó nevét szenvedő szerkezetben. Egy változást rögzítő változót például changed-nek, vagy isChanged-nek. A kis-nagy betűkkel szeparálhatjuk magunknak a változóinkat. Az állapotokat tároló változók (mint az előbb is volt) kezdődjön "is" -el. Az ilyen értékek többnyire binárisak lesznek (persze csak a mi szempontunkból. Bináris típus nincs Pythonban Szerk: Ekkora marhaságot hogy írhattam le?! Dehogy nincs bináris típus Pythonban). isChanged, isLoggedIn, isLoggedOut, hasonlók. Ezt követhetjük majd a függvényeink írásánál is. A beépített str() függvény sokkal kifejezőbb lenne egy toStr() névvel akár. Remélem érthető mindenkinek. Ha nem, akkor egyszerűen próbáljatok meg hasonlítani az én változóimra, aztán rátok ragad.
Ha éppen összeütünk valami kis kódot ebédre, nem kötelező ilyen szép rendezett változókat használni. Én rendszerint káromkodásokat szoktam változónévnek használni. Nem a legszebb, de gyorsan le tudom írni.
1.1 - A Python-os programozási stílus (indent block)
A Python kicsit eltér a többi programozási nyelvtől. Ezt tartsa mindenki olyan fejbe, akinek ez az első nyelve, és azok is, akik más nyelveket már ismernek.
A legtöbb programozási nyelvben, amikor egy blokknyi kódot definiálunk, azt {} karakterek közé írjuk. Ilyen blokkok majd az if-nél is előjönnek, ez lesz az a mag, ami a feltétel teljesülésekor lefut. Ezt meg kell határozni kézzel, az értelmező nem tudja helyettünk kitalálni.
Pythonban a {} karakterek helyett behúzással lehet blokkokat létrehozni, ezt indented block-nak hívja a nyelv. A behúzás remélem mindenkinek ismert, Microsoft Word-ből minimum. Behúzásokat a tabulátorral lehet létrehozni. Ez ugye 8 szóköznek felel meg, de csupán 1 szóköznyi távolság a margótól is már meghatároz egy blokkot.
Nem kell tartani a blokkoktól, a hétköznapi életben is lehet velük találkozni. A könnyebb megértéshez egy kis példa:
print "indent block test" if 2 > 1: print "we are in an if statement" for a in range(10): print a print "byebye" print "indent block test finished"
A fenti kódot színezzük még kicsit ki, hogy jobban látható legyen amit akarok:

A színezéssel az összetartozó részeket szeretném kiemelni. Jól látható, hogy gyakorlatilag szintek jönnek létre, ezek az indent level-ek. Azok a kifejezések, amiknek magjuk van, mind megkövetelik, hogy blokkba kerüljenek. A blokkokat pedig a margótól való távolsággal lehet definiálni. A futás közben az értelmező egyre beljebb ugrál a szintek közt. Amikor egy bekezdés kijjebb van a többihez képest, akkor tudja az értelmező, hogy kijutott egy magból. Fontos, hogy csak úgy nem lehet ilyen blokkokat létrehozni, hogy szeparáljunk kódrészleteket. Ha egy kód beljebb van a többihez képest, akkor az interpreter feltételezi, hogy a közelben olyan kifejezés volt, ami megkövetelte a behúzást. Ha nem létezett ilyen, akkor unexcepted indent hibát kapunk.
Igen könnyű a blokk létrehozásra szabályt létrehozni: Ha egy kifejezés végére ":" kerül, akkor azzal kinyitottunk egy blokkot, tehát a kifejezés (feltétel, ciklus, definició, akármi) magját beljebb kell folytatnunk az aktuális margótól. Ha végeztünk a maggal, vissza kell lépni az előző margóhoz. Ez egy egyszerű visszatörlés.
Azzal, hogy ilyen blokkokkal kell dolgozni, nagyon jól átlátható lesz a kód, viszont nem lehet az egész programot egy sorba írni.. ha netán valakinek ilyen igénye lenne.
1.1.1 A ";" karakter
Sok más nyelvben a kifejezések végét ";" (vagy valamilyen más) speciális karakter jelzi. Pythonban erre nincs szükség, látható, én se használom. Ha mégis használjuk, úgy több kifejezés is kerülhet egy sorba. Én ahogy idáig láttam, azt mondhatom, hogy nem általános a ";" karakter használata. Ráérünk trükközni vele mi is.
2 - Az "if" kifejezés
Vagy utasítás, így utólag átgondolva.
Tehát az "if". Most már tudjuk, hogy mikor kell blokkokat használni majd, így nem merül majd fel a kérdés: "Mégis mi a jóf#ért vannak ezek a sorok beljebb?"
2.1 - Mi a túró a feltétel magja?
Nem biztos, hogy mindenkinek érthető, ezért leírnám. Megírjuk nemsokára az első elágazásunkat. Ez egy feltétel lesz. A feltétel teljesülésekor azt szeretnénk, hogy egy adott kód fusson le. Ezt a kódot el kell különíteni a többi kódtól. Ez logikus, hisz ha a feltétel nem teljesül, akkor nem kell majd történnie semminek se! A feltételhez tartozó mag egy elkülönített kódrészlet. Esetünkben csak akkor kerül lefutásra, ha a feltétel teljesül. A ciklusok esetében ez, a magban levő kód annyiszor fut le, ahányszor a ciklus mondja neki. A függvények magjában levő kód csak akkor fut majd le, ha a függvény meghívásra kerül. Ha nem világos, akkor csak tessék figyelni :)
2.2 - Az IF
Az is kifejezés nem csinál semmi érdekeset, mint hogy a feltételébe egy logikai kifejezés kerül. Megnézi hogy a kifejezés értéke True-e vagy False. Ha True, akkor az IF-hez tartozó kód fut le, ha False, akkor halad tovább a kód.
A logikai kifejezésekről volt már szó (Python operátorok, típusok [part2]), ezekkel nem foglalkozok. A kifejezést a következőképpen kell felépíteni:
if feltétel: mag
Akkor írjuk is meg az első feltételünket. Egyszerűen: 2 nagyobb-e mint 1?
if 2 > 1: print "Ketto nagyobb mint egy!" #>>Ketto nagyobb mint egy!
Ilyen egyszerű az egész! A feltétel helyére bármilyen logikai kifejezést írhatunk, ez már csak a fantáziánkon múlik. Természetesen jöhetnek változók, minden, ezt nem is ismételgetném el. Használjunk zárójeleket, nagyban megkönnyítik az átláthatóságot!
2.3 - Az ELSE
Mi történik, ha a fenti kód nem teljesül? Így:
if 1 > 2: print "Ketto nagyobb mint egy!" #>>
Semmi nem kerül a képernyőre. Jogos, hiszen 1>2 az False. Ha a feltétel hamis, akkor nem lép be az értelmező az IF blokkba. A hamis logikai eredményeket az ELSE kifejezés kezeli le. Ez az IF kifejezésen kívül van (ha az IF-be lenne, akkor nem kerülne lefuttatásra, hiszen be se léptünk az IF-be!)
if 1 > 2: print "Ketto nagyobb mint egy!" else: print "Ez a feltetel nem igaz!" #>>Ez a feltetel nem igaz!
Az else kifejezéshez már nem kerül semmilyen feltétel. Ha egyszer se kaptunk True értéket, mindenképp az else ágra kerülünk. Tehát ha értékeket ellenőrzünk több szempont alapján, és egyik se teljesül, a legvégén az else ágba dobhatunk egy üzenetet hogy "öreg, semmi se jött össze neked".
2.4 - Az else if (ELIF)
Nagyon fontos, én mindig elfelejtem: Pythonban az else if-et elif-nek hívják.
Az else if-et magyarra valahogy úgy lehetne fordítani: "különben ha".
Ezt jó szem előtt tartani. Egy irtó béna példával szemléltetném:
szam = 5 if szam == 6: print "A szam erteke 6" elif szam == 7: print "A szam erteke 7" else: print "A szam erteke se nem 6, se nem 7" #>>A szam erteke se nem 6, se nem 7
Szavakkal: ha a szam értéke 6, akkor az első ágba ugrunk. Különben ha a szam értéke 7, akkor a 2. ágba. Ha egyik se jött be, akkor kiírjuk hogy a szem értéke se nem 6, se nem 7.
Nem kötelező elif-et használni. Megtehetjük, hogy minden feltételt szimpla if-el teszünk fel, és csak a legutolsó if kap else ágat. Az eredmény ugyan az lesz, ez így viszont szebb.
2.6 IF példa (oszthatóság)
Az oszthatóságot szokás az IF-nél mutogatni, úgyhogy én is így tennék. Állapítsuk meg egy számról, hogy osztható-e 6-al maradék nélkül. Ehhez csak a maradékát kell képeznünk a számnak 6-al, és ha az 0, akkor igaz a feltétel, és örülünk. Tessék:
if 120 % 6 == 0: print "120 oszthato 6-al" #>>120 oszthato 6-al
Ebben semmi kihívás nem volt. Most oszthatósági szabállyal oldjuk meg a problémát. Ez a 6 esetében ugye az, hogy a szám osztható legyen egyszerre 3-al ÉS 2-vel. A feltétel kimondásakor tehát egyből két feltételt fogalmaztunk meg, és azt egy ÉS kapcsolóval kapcsoltuk össze. Ez a programban se fog máshogy kinézni:
if (120%3==0) and (120%2==0): print "120 oszthato 6-al" #>>120 oszthato 6-al
2.7 IF példa (bekért adatra)
Mivel az előző bejegyzésben megtanultuk a raw_input-ot, így már a felhasználótól bekért adattal is dolgozhatunk. Kérjünk be egy számot, és írjuk ki, hogy osztható-e hattal!
ertek = int(raw_input("Irj egy szamot: "))
if ertek % 6 == 0:
print "A %s oszthato 6-al" % ertek
elif ertek % 7 ==0:
print "A %s nem oszthato 6-al, de 7-el igen!" % ertek
else:
print "A szamod se 6-al, de 7-el se oszthato"
#>>Irj egy szamot: <<12
#>>A 12 oszthato 6-al
Remélem nem feledkeztetek meg ti se arról, hogy a raw_input()-ot int()-be tegyétek. Itt látszódik: stringből nem lehet maradékot képezni. Oda kell figyelni a típusokra.
A kód kiír még egy-két apróságot, ami előhozhat egy problémát. Ugyanis a kód figyeli a 6-al és a 7-el osztható számokat is. Mi történik, ha 42-t írunk be? Számíthatunk arra, hogy a program a semmiből kiírja, hogy beírtuk "a választ a kérdésre", de a valóságban csak annyit kapunk, hogy a szám osztható 6-al. De mégis miért? Hisz 6-al és 7-el is osztható.
A lényeg a feltételek sorrendjén van. Amint egy feltétel teljesül, a többi nem kerül vizsgálatra! Mivel rögtön az első (szam % 6 == 0) True, ezért csak ezt látjuk a képernyőn. Ezeket megelőzően egy feltételben figyelni kell, hogy a szam osztható legyen 6-al és 7-el (vagy egyből 42-vel, ez gondolkodás kérdése innentől). Tehát a feltételek alkotásánál figyeljünk, hogy folyamatosan szűkítsük a kört!
2.9.1 Minden IF kiváltható még egy AND-el!
Ilyen gondolatok, mint hogy egymásba ágyazott if-ek remélem nem rémisztenek meg senkit. Maradjunk a 42-vel oszthatóságnál. Kinézhet a következő gondolat szerint is:
if (42%6==0): if (42%7==0): print "42 oszthato 42-vel" #>>42 oszthato 42-vel.
Először megállapítjuk a számról, hogy osztható-e 6-al. Ha igen, akkor utána azt is, hogy osztható-e még 7-el is. Ha igen, akkor győzelem.
Az ilyen feltételeket mindig össze lehet hegeszteni úgy, hogy egy if-el megoldható lehessen a probléma. Hisz a feltétel, mint már sokszor írtam az: "Ha 42 osztható 6-al ÉS 42 osztható 7-el, akkor 42-vel is osztható". A 2.6-os bekezdés végén, bár 120-al, de be van ez is bizonyítva.
Igyekezzünk mindent lehetőleg 1 kifejezésbe belerakni, kivétel persze ha olvashatóbb a kód a sok egymásba ágyazott IF.
Személyes élményem, hogy a szoftver labor 1 nagyházimban levő billentyűzetkiosztás lekezelésénél minden vezérlőbillentyűt ki kellett kerülnöm. Ez kb 10 billentyű volt, ami sokkal olvashatóbb lett egymás alá leírt 10db if-el, mint egy sornyi and-el (megj: látszik a Pythonos beidegződés: C-ben meg lehet csinálni, hogy zárójelben sort török. Ez akkor nem jutott eszembe, mivel Pythonban nem lehetséges az ilyen, hisz akkor megtöröm a blokk egységét).
2.8 IF extrák Python-ban (6 > x > 2)
Idáig nem találkoztam olyan nyelvvel, amiben egy feltételben két értéknél több szerepelhetne. Rendszerint egy értékhez viszonyítunk egy másikat, nagyobb-e, kisebb, egyenlő. Pedig az iskolába meg lehetett úgy határozni az x értékét, hogy két relációs jel közé kerül (6 > x > 2).
Ez sok nyelvben nem járható út, kivétel Pythonban! Amikor egy logikai kifejezésben ugyan azon az értéken két különböző vizsgálatot végzünk, és azokat AND operátorral kötnénk össze, akkor elhagyhatjuk azt a halom karaktert, és hétköznapi relációként felírhatjuk a kifejezésünket. Egy példával sokkal egyszerűbb lesz:
print (5>3) and (3>2) #>>True print 5 > 3 > 2 #>>True
2.9 És mi a helyzet a SWITCH-el?
Sok más nyelvben létezik a switch utasítás. Ezzel sok felesleges if-el kezdődő sort lehet megspórolni, viszont a Python nem ismeri ezt a kifejezést. Trükközéssel megoldható (függvénypointerek), de úgy általában nem létezik.
3 - Zárás
Az IF egy igen egyszerű utasítás mint látható. Nem akartam több tonnányi példát hozni, mert nincs miért. Nincs például olyan tiltás, hogy if-be nem kerülhet raw_input(). Akkor akarsz bekérni egy számot amikor egy feltétel nem teljesül? Hát beírod az else ágba. Nincs itt semmi különleges. Össze akarsz hasonlítani két stringet? Ez Pythonban nem probléma. Azért itt megjegyezném, string-nél csak egyezést lehet megállapítani. Relációkkal (> , <) nem a stringek hosszait hasonlítjuk össze! De ezt már azt hiszem írtam is.
A következő bejegyzésben a ciklusoknak indulunk neki. Érdemes lesz figyelni a bejegyzések elejét: ha valami olyan kerül egy leírásba, amit illik globálisan tudni, akkor az kiemelésre kerül.
Ja igen, és megírom végre a string-ekről szóló teljes postot.
-slp
print , raw_input
A sikeres programozás vizsgám utáni ünneplésből visszatérve akkor tehát lássuk az előző bejegyzés végén említett két függvényt.
0 - Ebben a fejezetben
2.1 - A print használata
2.1.2 - Python format string
2.1.3 - Kilépő- és védőkarakterek
2.1.3.1 - Aposztróf és idézőjel
2.2 -A raw_input() használata (jól, jobban, még jobban)
1 - Miért ezeket a függvényeket?
Őszintén megmondom, ilyenkor az elágazásokat és a ciklusokat szokás venni, de sokkal izgalmasabbnak tartom, ha előbb ezt a két függvényt magyaráznám el. A print -et már csomószor kellett használni, így ideje lenne megismerni részletesebben is őt.
A raw_input a felhasználói interakció létrehozásához kéne. Szép magyarul mondva: hogy a felhasználótól bekérhessünk valami adatot. Szerintem sokkal menőbb ha először megtanuljuk hogy hogyan kell a felhasználótól származó adattal bánni, és utána majd nekiállhatunk a többi jóságnak is.
2 - A print
A print a Python 2.x -ben igazából kifejezés, tehát statement. Python 3.x-től a print függvény már. Én, mint ahogy az alapításkor is írtam, a Python 2.x szabályai szerint fogom írni ezt a leírást.
Tiszta sor, hogy idáig is használtuk már a print kifejezést, igazából csak pár érték megjelenítésére a konzolba. Most minden akkor felmerülő "miért?"-re választ fogunk kapni.
Az első kérdés talán az lehet: Mit is csinál pontosan a print? A válasz egyszerű: az alapértelmezett kimeneten (ez a konzolablak) egy szöveget jelenít meg, és a kiíratás végén sortörést végez. Hogy mi az a sortörés, kiderül a leírás végére!
2.1 - A print használata
A print kifejezés után szóközzel következzen egy string. A stringet ugye " " közé szokás írni. Így létre is jön az első szövegünk kiíratása, stílusosan legyen Hello World!
print "Hello World!" #>>Hello World!
Nem csak stringek kiíratására jó a print, ezt már korábban bebizonyítottam. Ez a kifejezés bármit képes a képernyőre kiírni, ha mást nem, akkor egy memóriacímet, de valamit mindig tudni fog.
Ezekből kiindulva bátran írjunk be integert, float-ot, vagy mi épp eszünkbe jut. Ne feledkezzünk meg róla, hogy ugye ezek nem stringek, így nem kell őket " " közé írni!
print 3.14 #>>3.14 print 1337 #>>1337
Változó értékének kiíratása se túl bonyolult, szimplán a változó nevét kell leírni. Ha több változó értékét akarjuk egyszerre megjeleníteni, akkor soroljuk fel a változókat vesszővel elválasztva. Nem kell aggódnunk a típusok miatt ebben az esetben. A képernyőn a megadott változó értékei fognak megjelenni, és a kódban szereplő vessző karakter helyett gyakorlatilag szóközt fogunk kapni a kimenetre.
ing = 5 str = "alma" print ing,str #>>5 alma
Természetesen változók és konstansok egyaránt szerepelhetnek egy kifejezésben, ezzel nincs semmi probléma. Ha a fenti formát választjuk (vesszővel elválasztás) akkor a típusok miatt sose kell aggódni, de így minden egyes értéket szóközzel elválasztva fogunk megkapni!
ing = 5 stg = 6 print ing,"+",stg,"=11" #>>5 + 6 =11
Szabadon írhatunk a vesszők elé-mögé szóközöket, ha azok megkönnyítik az olvashatóságot.
Teljes listákat is megjeleníthetünk, ilyenkor a kódban levő listát ugyan olyan formában kapjuk a képernyőre, mint ahogy a kódba le van írva:
lista = [5,"alma"] print lista #>>[5 ,'alma']
Fontosabb dolog hogy mit tegyünk akkor, ha nem akarunk felesleges szóközöket látni a kimeneten. Ilyenkor két megoldás jöhet szóba, az egyik a típuskonverzió, a másik a format string (formátum string, csodás magyar fordításban).
2.1.1 Típuskonverzió a print-ben
Ez a téma már nem új egyáltalán, az előző bejegyzésben végig tárgyaltam a típuskonverziót, amennyire ezt Pythonban tudni illik. Most gyakorlatilag csak átismételném.
A lényeg ugye az, hogy tudnunk kell azt a pontot, amikor már a módosított típusú értékekkel akarunk dolgozni. Ha számokat és stringeket akarunk egy printbe tenni, akkor elég csak a print-nél végezni típuskonverziót.
Amikor egy értéket string-é alakítunk (str() függvény), onnantól hozzá adhatjuk egy másik stringhez. Ilyenkor, ha csak mi nem tettünk szóköz karaktert valamelyik stringbe, nem látszódik majd, hogy hol lett összeragasztva a két string. Tehát a print-be minden értéket ami string-től eltér, string-é alakítunk, és összeadjuk őket egymással.
ing = 5 stg = "alma" print str(ing)+stg #>>5alma
Ez az egyik járható út, viszont előkerül egy problémás szituáció, a tört számok kiírása. Ugyanis az str() függvény gyakorlatilag a képernyőre kikerülő értéket alakítja string-é. Ha egy tört szám 10 tizedesjegy pontosan kerül a képernyőre úgy az string-be is így fog kinézni. Példa:
flt = 7./3 print flt #>>2.33333333333
Ilyen pontos számokra nincs szükségünk. Lehetne trükközni hogy levágjuk a végét ehhez hasonlóan:
flt = 54./25 print str(flt)[:4] #>>2.16
De ez teljesen felesleges, van erre az egész típuskonverziós mizériára egy sokkal jobb módszer, ez pedig a format string.
2.1.2 Python format string
A format string C-s örökség, nagyon sok nyelv ismeri. A lényege az, hogy létrehozunk egy olyan stringet, amikben speciális karakterek vannak. A print kifejezés a string kiírásakor ezeket a speciális karaktereket egy listában megadott értékekre kicseréli. Ennyi az egész móka.
A Python részéről 2 féle format string-et ismer, ez a régi C stílusú, illetve a string.Template. Én az előbbit írnám most, ugyanis én is ezt használom :)
A print esetében mostantól háttérbe szorulnak a típuskonverziót végző (str(), int(), float() ...) függvények. Helyüket a következő "szövegek" veszik át:
Nem olyan túl bonyolult megjegyezni őket. Hangsúlyoztam, hogy ezek "szövegek", tehát: kezdjünk el írni egy stringet, és ahol változó értékét szeretnénk majd megjeleníteni, ott a stringbe írjuk bele a megfelelőt a fenti 3 (hívjuk így:) format string közül. A kiírandó string lezárása után következik a paraméterlista.
A paraméterlistába kerülnek azok az értékek (és változók is persze), amik helyét jeleztük a stringen bellül. Fontos, hogy a sorrend, és a format stringek és a paraméter lista elemeinek száma megegyezzen. Gondolom ez logikus is, máskülönben nem lehetne párba állítani őket.
Alkalmazva az alábbi szerint néz ki:
A paraméterlistába nincs semmi újdonság, az egyes elemeket vesszővel választjuk el egymástól.
Egy működő példával így néz ki:
ing = 5 stg = "alma" print "%i%s" % (ing,stg) #>>5alma
Gyakorlatilag nincs szükség arra egyébként, hogy integer kiírásánál a %i tagot használjuk, de ha ragaszkodunk hozzá, akkor lehet. Bővel elég a %s is.
Ugye a float-ok miatt keveredtünk bele az egész format stringes témába. Lehetőség van rá, hogy megadjuk, hogy hány karaktert akarunk látni a kiírandó értékből.
print "%.2f" % (54./25) #>>2.16
Azaz a % és a f jel közé beékelődik egy .számjegyek, ami azt jelzi egyszerűen, hogy a pont után jobbra hány számjegyet akarunk látni. Érdekességként megjegyzem, hogy ez működik stringekkel is, ilyenkor a kiírandó betűk számát jelöli. Ezt igazából nem szokás használni, de van rá lehetőség.
Egyszerű formázást lehet elérni akkor, ha a ponttól balra helyezünk el számot. Ez gyakorlatilag azt jelenti, hogy a kiíratás jobbról-balra történik.
print "%5.2f" % (54./25) #>> 2.16
Mint látható, az utolsó számjegy 5 karakter távol legyen, és innen kiíratunk még 2 tizedesjegyet is. Ez így tizedesponttal, mindenestől 4 karakter, tehát 1 üres szóköz karakter marad a konzol széle és az első számjegy közt.
A fejezet lezárásául egy rövid példa egy "feladat" típuskonverzióval és format stringgel való megoldására. Először a típuskonverzióval:
year = 2010 month = "11" day = "24" day_name = "Wednesday" print "Ev: "+str(year)+" Honap: "+str(month)+" Nap: "+str(day)+" : "+day_name #>>Ev: 2010 Honap: 11 Nap: 24 : Wednesday
Ez pedig a format stringgel:
year = 2010 month = "11" day = "24" ay_name = "Wednesday" print "Ev: %s Honap: %s Nap: %s : %s" % (year,month,day,day_name) #>>Ev: 2010 Honap: 11 Nap: 24 : Wednesday
Remélem nem csak nekem könnyebb az utóbbit elolvasni, ráadásul rövidebb is... egy picit :)
2.1.2.1 Mikor mit használjunk?
A bőség zavarában felmerülhet a kérdés, hogy mikor mit kell használni. Részemről: ha csak gyors szükségem van egy értékre magamnak, vagy a program nem igényel semmilyen kulturált megjelenést a kimeneten, akkor nem szükséges a format stringet használni. Amikor kész üzeneteket írunk, akkor használjuk őket. Persze a programozás közben ezt tudjuk lehetőleg előre, máskülönben megtapasztaljuk hogy milyen bazi sokáig tart minden printet kicserélgetni.
2.1.3 Kilépő- és vezérlőkarakterek
Tegyük fel, hogy egy stringbe egy idézőjelet akarunk tenni. A következőképpen néz ez ki:
stg = " " "
Ez nekünk embereknek teljesen érthető, viszont a számítógép szempontjából a 2. idézőjel lezárta a stringet. A 3. idézőjelnél pedig hibát kapunk. Mi tehát a teendő, ha olyan karaktereket akarunk kiírni, amikkel a programunk működését befolyásoljuk? (Az ilyen karaktereket kilépőkaraktereknek hívják).
Egy védőkarakterrel lehet megakadályozni a "kiugrást" a stringből. Ez a "\".
A \ jel utáni első karakter kiírásra kerül, kivétel ha az valamilyen vezérlőkarakter. Tehát ha olyan karaktert akarunk kiírni, ami módosítja a programot, írjunk a karakter elé egyszerűen \ jelet, így:
stg = "\"" print stg #>>"
Ha "\" jelet akarunk írni, akkor a kódba dupla ("\\") jelet kell írni. Érthető, hisz az első \ jel csak figyeli egyelőre, hogy utána milyen karakter áll. Ha ez még egy \ jel, akkor az kerül kiírásra. Léteznek viszont más karakterek is, amik a \ után állhatnak, ezek a vezérlőkarakterek.
Ezek bár két karakterből állnak, egy karakternek szokták említeni őket (mivel a valóságban mindegyik egy-egy ASCII kód).
Feljebb írtam, a format string-nél, hogy %-jelet %%-al lehet kiírni. Ez csak akkor igaz, ha a string egy format string. Amennyiben nem az, úgy nem kell dupla százalékjelet írni!
A legtöbbet előkerülő karakter a \n, a sortörés. A szöveg kiírásakor itt új sor kezdődik, és ott folytatódik tovább a kiírás. Mint ismeretes, a print mindig sortöréssel záródik le, tehát ha két sor szöveget akarunk megjeleníteni, akkor azt két print-el is megtehetjük, vagy fessebb, ha egy print-el, és az első sor végén \n karakterrel sort törünk.
Azaz:
print "Elso sor" print "Masodik sor" #>>Elso sor #>>Masodik sor
Lehetséges így is:
print "Elso sor\nMasodik sor" #>>Elso sor #>>Masodik sor
Igyekezzünk rászokni erre a formára, egy egész print-et sikerült kikerülni!
Kocsivissza (\r) karaktert akkor szokás használni, ha egy meglevő szöveget felül akarunk írni. Ilyenkor a kurzor a konzolban visszakerül a legutoljára kiírt sor elejére. Az ez után következő kiírás INSERT szerű, tehát a meglevő szöveg nem kerül eltolásra, az új karakterek egyszerűen felülírják a meglevőket:
print "Elso sor\nMasodik sor\rAlma" #>>Elso sor #>>Almadik sor
2.1.3.1 Aposztrof kontra idézőjel AKA tudatos programozás
Kis előrelátással csomó felesleges "\"-től óvhatjuk meg magunkat, ugyanis Pythonban stringet kétféleképpen lehet meghatározni. Egyfelől az idáig sokat emlegetett idézőjellel (") és a másik, hogy szimpla posztroffal ( ' ). Igazából Pythonban ez utóbbit szokás használni, viszont sok más nyelvben inkább az idézőjelet alkalmazzák, ezért használom én is az előbbit.
A tudatos programozás ott lép közbe, hogy az angol nyelvben a ' ' jelekkel szokás idézni, míg a magyarban " "-el. Ezek a karakterek csak akkor játszanak kilépőkarakter szerepet, ha a stringben leírt karakter megegyezik a string kezdő- és lezárókarakterével. Leegyszerűsítve: ha tudjuk, hogy a stringbe ' karaktert fogunk leírni, akkor a stringet " " karakterek közt határozzuk meg (a " "-ből nem lép ki az '). Értelem szerűen a fordítottja is igaz (ha a stringbe " " karakter lesz, akkor ' ' közt határozzuk meg).
Példával:
print "What's up?" #>>What's up?
Ha aposztroffal kezdtük volna, akkor így nézne ki:
print 'What\'s up?' #>>What's up?
Megspóroltunk egy \ karaktert.
2.2 A raw_input() használata
Hirtelen véget is ért a print kifejezés magyarázata, és rögtön át is térünk a raw_input()-ra. A raw_input() egy függvény lesz, amivel egy jó ideig a beolvasást fogjuk elvégezni a standard bemenetről, azaz a billentyűzetről. Tehát beírunk valamit a programunknak. Lássuk csak, hogy mire képes!
2.2.1 Amit a raw_input()-ról tudni kell
A raw_input() legfontosabb tulajdonsága, hogy bármit is írunk be neki, az egy string lesz. Mostantól lesz valamivel fontosabb az oly' sokat emlegetett típuskonverzió, ugyanis a Python beépített, és majdani saját függvényeinknek egyáltalán nem mindegy, hogy számot vagy szöveget adunk be értékül.
2.2.2 raw_input() élesben I.
Amikor egy változónak "értékül adjuk" a raw_input()-ot, akkor a kód futása ezen a ponton meg fog állni, és egy villogó kurzor kerül a képernyőre, ami várja a befelé érkező adatot. A változó, aminek "értékül adtuk" a raw_input()-ot fogja tartalmazni az enter billentyű leütése után azt a stringet, amit beírtunk. Fontos, hangsúlyozom: ha számot írunk be, az is stringként fog szerepelni!
Eléggé nagyok vagyunk már, úgyhogy az "értékül adjuk" helyett mondjuk azt, hogy a raw_input() visszatérési értékével dolgozunk. Bár a függvényekről nem volt még szó, fontos: amikor egy változó értéke egy függvény lesz, akkor a függvény lefutása után, a függvény által visszadobott értéket tároljuk majd el a változóban. Ezt sokkal bonyolultabb leírni mint megcsinálni, úgyhogy nem kell aggódni, a kódból minden világos lesz!
Elég a mellébeszélésből, kérjünk be egy értéket:
ertek = raw_input() print ertek #<<Hello #>>Hello
A program látszólag nem csinál semmit, de ha beírunk egy értéket neki, utána ugyan azt az értéket ki is íratjuk, ezt csinálja a print ugye. Egy normális programban felszólításra kerül a felhasználó az adat bevitelekor (hogy mégis mit vár a programunk). Egyszerűen kiírjuk tehát a felszólítást az érték bekérése előtt. Nem kell megijedni, az ertek nevű változóba kerül a begépelt string. Ezzel nyugodtan játszadozhatunk ez után is, tehát a kiíratáskor ragasszunk elé egy olyan feliratot például hogy: "A begepelt szoveged a "" (begépelt szöveg) "" volt". Itt lesz aztán minden : )
print "Irj be egy mondatot!" ertek = raw_input() print "A begepelt szoveged a \"" + ertek + "\" volt" #>>Irj be egy mondatot! #<<Lorem ipsum #>>A begepelt szoveged a "Lorem ipsum" volt
Látszik milyen hülye voltam, aposztroffal kellett volna kezdeni a print-ben levő stringet. Nem baj, így láttunk \ jeleket is.
Ilyen egyszerű a raw_input() működése első körben, de lehet ezt még cifrázni.
2.2.3 A raw_input() élesebben II.
Ha nem tűnt volna fel, a raw_input() mindig új sorban kérte be idáig az adatokat tőlünk. Erre az az egyszerű magyarázat, hogy a print kifejezés, mint mondtam, a lefutása végén sort tör. Szerencsére a raw_input() is képes kiíratásra, aminek a végén egyfelől nincs sortörés, másfelől kikerülhető vele egy print! Írjunk a raw_input() paraméterébe egy stringet, és kész is!
ertek = raw_input("Irj be valamit: ")
print "Pont ezt irtad: " + ertek
#>>Irj be valamit: <<Pythonblog
#>>Pont ezt irtad: Pythonblog
A print-nél megtanult format string itt is működik. Egyedül annyit kell fejben tartani, bár ez megint logikus, hogy a format string paraméterlistája a raw_input zárójelei között legyen (azért logikus, mert hogy a zárójelek lezárják a string hatáskörét).
Nem túl fantáziadús, de példának megteszi:
szoveg = "Irj be valamit: "
ertek = raw_input("%s" % (szoveg))
print "Pont ezt irtad: " + ertek
#>>Irj be valamit: <<Pythonblog
#>>Pont ezt irtad: Pythonblog
2.2.4 A raw_input() típusának módosítása
Ezzel a bejegyzéssel befejezném a típuskonverzió erőltetését, hiszen itt nyeri el végre az értelmét.
Adott a következő szituáció:
Be szeretnénk kérni két számot, és kiírni az összegüket. Mi sem egyszerűbb ennél:
elso = raw_input("Elso szam: ")
masodik = raw_input("Masodik szam: ")
print "A ket szam osszege %s" % (elso+masodik)
#>>Elso szam: <<5
#>>Masodik szam: <<6
#>>A ket szam osszege 56
(Itt már figyeltem hogy format stringgel írjam ki az eredményt, elvégre róla beszéltem egész idáig :)
Mint látható, baromságot kaptunk eredményül: 56. Persze ez csak a várt eredmény szempontjából baromság, mert amúgy érhető hogy mi történt: bekértünk két értéket. Mindkettő string, ha két stringet összeadunk, akkor "összeragadnak". Így tehát ez 56, nem 11.
A megoldás egyszerű, a raw_input() visszatérési értékének típusát módosítjuk egy típuskonverziós (int(), float()) függvény segítségével. A legegyszerűbb, ha a kész raw_input()-unkat berakjuk a megfelelő típuskonverziós függvénybe utólag eleinte, persze később ha már csípőből megy a téma, akkor írjuk egyből.
Ezek tekintetében a kód így néz ki:
elso = int(raw_input("Elso szam: "))
masodik = int(raw_input("Masodik szam: "))
print "A ket szam osszege %s" % (elso+masodik)
#>>Elso szam: <<5
#>>Masodik szam: <<6
#>>A ket szam osszege 11
Ilyen egyszerű az egész!
2.2.4.1 Mikor módosítsuk a típust?
Szokás szerint kiemelném ezt a pontot megint. Mint régebben mondtam: "csak ott módosítsunk típust, ahol már az új típusra szükségünk van". Ez idáig rendszerint a kiíratás pillanata volt. Most a szituáció az, hogy amint bekértünk egy értéket, onnantól azonnal a megfelelő típusúként szeretnénk látni. Ez az indok arra, hogy a raw_input()-ot azonnal egy int() vagy float()-ba bele is rakjuk (str()-t nincs értelme használni, hiszel eleve ilyen típusú az érték).
2.2.5 "Írjon be egy számjegyet: kettő"
Mint kiderült az idők során, nem mindenkinek evidens a program felszólításai elleni lázadás, engem azért ha egy konkrét adattípus bevitelére kér a program, mindig kipróbálom, hogy mégis mi történik, ha nem a várt adatot írom be. A címben szereplő példa klasszikusnak tekinthető. Az "Írjon be egy számjegyet" felszólításból egyértelmű, hogy integert várunk. Beírjuk hogy "kettő". Erre a program hibával kilép.
Az ilyen hibák kezelésére lehetőség van, ez a kivételkezelés, vagy exception... de ez egy másik rész lesz :)
3 - Zárás
Remélem hogy nem felejtettem ki semmi fontosat. Jó lassan készült el ez a bejegyzés, de csak hogy védjem magamat: nincs h billentyű a laptopomon, ezért igen nehéz leírni ezt a betűt. Bátran kommenteljetek! Gyakran átolvasom a bejegyzéseimet, és ha valami saját magamnak nem tiszta, vagy hiányolok, azt beleírom a bejegyzésbe. Ha valami nektek nem tiszta, írjátok le, mert a kommentek a később ide tévedőknek is segítenek majd!
Most hogy ki tudunk írni és bekérni profi módon, ismerjük már a logikai és műveletvégző operátorokat, ideje hogy a programunk gondolkozzon. A következő fejezetben az elágazásokról lesz szó, a híres if kifejezésről.
-slp
Python operátorok, típusok [part2]
Elkezdődött a suli, tehát időszerű itt is valamit tanulni. A következőkben az operátorokról lesz szó először, aztán vissza térünk a változókra.
0. Ebben a fejezetben
1 - Operátorok (műveletvégző és logikai operátorok)
2.1 - Automatikus típuskonverzió
2.1.1 - Float-ok rövidítése
2.2 - Felsorolt típusokról bővebben
2.2.1 - Indexelés - fordított indexelés
2.2.2 - Tömb szeletelése (slice)
1. Mik azok az operátorok?
Az operátorok tradicionálisan műveleteket takarnak. Olyan műveleteket, amiket a számítógép el tud végezni. Fontos hogy ide csakis alapműveleteket képzeljünk. Például van program, ami az integrálás műveletét képes elvégezni, viszont ez is valamilyen egyszerű operátorok véges ismétlésének az eredménye lesz. Amikor saját függvényeket hozunk létre, olyan dolgokat amik valami komplexebb feladat elvégzésére lesznek képesek, a függvényben magában ezeket az egyszerű műveleteket kell majd alkalmaznunk. Ahhoz hogy ebből működő program is legyen, ahhoz csak a logikánkat kell majd használni.
Na de ne beszéljünk ilyen személytelenül az operátorokról, lássuk őket.
1.1 Műveletvégző operátorok
Tehát akkor egy példa, a könnyítés végett:
13'3'7 : 3 = 445.
1 3
1 7
2 << Ezt a számot kapjuk ezzel a művelettel
1.2 Logikai operátorok
A fenti operátorokkal egy csinos számológéppé léptettük elé a Pythont, most ideje hogy programozási nyelvé emeljük. A logikai műveletek azok a műveletek, amik közben a számítógép valójából elkezd gondolkozni. Tehát itt kezdődik a Skynet.
Mint ismeretes, a számítógép a bűvös kettes számrendszert használja. Egyesek és nullák, remélem nem új a téma. A logikai műveletek során mi gyakorlatilag egyértelműen eldönthető kérdéseket teszünk fel a számítógépnek, amire ő igennel vagy nemmel tud felelni, vagy inkább igaz és hamis, angolul True és False, számosítva 1 és 0. Meg is érkeztünk a kettes számrendszerhez.
Az ilyen kérdésekre (hívjuk feltételnek) kapott válaszokkal tudunk dolgozni. Nézzük milyen operátorok tudnak segíteni ebben:
Ez az operátor magyarul "vagy"-ot jelent. A két oldalán álló feltételek közül vagy az egyik, vagy a másik teljesül, akkor már az egész kifejezés igaz.
1.3 Operátorok élesben (logikai)
Sajnos ez igen száraz téma így, ezért legalább nem próbálom meg oldalakon keresztül szemléltetni. Igyekezzetek megérteni a működését. Ha most nem megy, nem kell elkeseredni, bőven ráértek út közben megérteni.
Talán még nem említettem, de a print kifejezés (vagy függvény) aztán akármit ki tud írni a képernyőre, még olyan dolgokat is, amiket nem lehetne. Ilyenkor legfeljebb valami memória címet, és egy rövid utasítást kapunk a kiíratott objektumról.
A logikai kifejezések végeredményeit is kiírhatjuk a print-el, ezzel szeretném szemléltetni az egyes operátorok működését röviden. Ilyet a valóságban gyakorlatilag soha nem csinálunk. A logikai értékeket többnyire egy elágazásnak adjuk át, mondjuk a híres if-nek, de ő majd a következő részben kerül szerepbe!
print 6 > 1 #>> True print 6 < 1 #>> False print (5==1) or (6>1) #>> True print (5==5) and (6<1) #>> False print ((5==5) and (6<1)) or (1==1) #>> True
Remélem minden sor érthető. Az utolsóhoz fűznék egy kis megjegyzést.
Látható, hogy egy halom kifejezés van az első nagy zárójelen belül. Az or operátor jobb oldalán az (1==1) kifejezés logikai értéke igaz. Mivel az or operátornak bármely tagja True, úgy az egész kifejezés True lesz, így tehát tök minden hogy mit trükköztünk az első nagy zárójelben, ez a kifejezés mindig igaz fog lenni. Így működik az egyik legalapabb SQL Injection támadás is, csak hogy tudjátok.
1.4 Értékek módosítása (műveletvégző)
Különböztessük meg egymástól mostantól azt, amikor egy változó értékét módosítjuk, és amikor új értéket adunk neki. Az érték módosítás legyen az, hogy a változó meglevő értékével csinálunk valamit. Az értékadás legyen az, hogy a meglevő értéktől teljesen függetlenül más értéket, sőt, akár más típusú értéket is teszünk mostantól a változóban.
Tételezzük fel, hogy egy változó értékét meg akarjuk növelni egyel. Tudjuk hogy a változó értékadásához kell a "=" operátor, és a "+"-al tudjuk majd megnövelni az értékét. A lényeges momentum itt az, hogy a megnövelt értéket újra tárolnunk kell a változóban, méghozzá abban, aminek az értékét növelni akartuk. Gyakorlatilag tehát egy változónak értékül adjuk önmagát és még egy egyest.
szam = 5 szam = szam + 1 print szam #>> 6
Fontos tehát, hogy ha egy változó értékét módosítjuk, akkor a kifejezés írásában illik szerepelnie annak a változónak, aminek az értékét módosítani fogjuk.
1.4.1 Rövidebb formula
Létezik egy rövidített formája is az értékek módosításának. Abban a speciális esetben használható, amikor az egyenlőség jel két oldalán ugyan az a változónév szerepel közvetlenül. Ilyenkor a jobb oldali változó neve elhagyható, és az egyenlőség jel bal oldalára kerül át az operátor. Egy példával sokkal egyszerűbb:
szam = 5 szam += 1 # szam = szam + 1 print szam #>> 6
Bármelyik műveletvégző operátort tehetjük a jel bal oldalára, még a hatványozást is, akármennyire is furcsán néz ki:
szam = 2 szam **= 10 # szam = szam ** 10 print szam #>> 1024
1.4.2 A még rövidebb forma (c++, c--)
Eléggé gyakori rutin a programozásban egy változó értékének egyel való növelése illetve csökkentése. Sok nyelvben erre azt a megoldást találták ki, hogy a változó neve után a műveletvégző operátor duplán szerepel, mint ahogy a címben is (c++, c--, sőt, ++c, és --c is létezik). Pythonban ilyen viszont nem létezik, így erről le kell tennünk. Az 1.4.1-es rész első példájában levő módszert kell alkalmazni az érték 1-el való módosítására.
2 Változók folytatása
A következőekben röviden szeretném befejezni azt, amit globálisan tudni illik a változókról. Az egyes típusok jellemzése külön-külön érdemesebb inkább.
Mivel Pythonban szinte minden objektum, így egy adott változó típusnak sok saját függvénye lehet, ezek a metódusok. Sok más nyelvben külön függvényeknek kell paraméterül adni egy-egy változót, hogy azon valamilyen műveletet elvégezhessünk. Gyakorlatilag ezek Pythonban is megvannak, csak minden egyes változó létrehozásával magában "a változóban benne lesz a függvény". Ez lesz a típus metódusa.
Persze ha a fenti sorok nem érthetőek, akkor megint nem kell pánikba esni, a későbbi példákból minden nyilvánvaló lesz.
2.1 Az automatikus típuskonverzió
Az első részben felsoroltam három olyan függvényt, amikkel egy változó típusa módosítható. Léteznek olyan speciális esetek, amikor a Python képes automatikus típuskonverzióra, ez főként az integer és float közötti átjárásban tud segíteni.
Az alap szituáció a tört számoknál jön létre. A tört számokkal mindig csak baj van, mert nem elég pontosak, vagy túlságosan pontosak, ez minket most igazából nem fog érinteni.
Tehát ha egy kifejezésben két integer típusú számot osztunk el, akkor egy integert kapunk eredményül, ami nem kerekített érték. Ezt írtam feljebb a "/" operátornál. Fontos viszont, hogy ha egy műveletben egy float és egy integer találkozik, akkor az eredmény float fog lenni, tehát itt automatikus típuskonverzió történt. Nem úgy kell gondolni a float-ra, hogy ha megjelenik a kifejezésben, akkor minden számot float-á alakít, mint valami kapcsoló. Egy példával szemléltetném:
print (7.0/2)*(2/4) #>>0.0 print (7.0/2)*(2.0/4) #>>1.75
Egyszerűen, mivel a jobb oldali zárójelben levő művelet integerekkel történt, és így 0-át kaptunk eredményül, a tört számot felszoroztuk nullával. (Ugye azért kaptunk a jobb zárójelben 0-t eredménynek, mert törtként ez 0.5 lenne, de lemaradnak a tizedes jegyek. Látszik: nincs kerekítés!)
2.1.1 Float-ok rövidítése
Akinek nem tűnt volna fel, egy számot az tesz float-á, hogy tizedespont van benne, nem pedig az, hogy utána szám áll. Jó, a 0 is szám igaz, de egy változó már a tizedesponttól törté változott. Tehát a nullás számjegy elhagyható, csak a pontra lesz szükségünk, így:
print (7./2)*(2./4) #>>1.75
Megjegyzés: A magyar nyelvben tizedesvesszőnek szokás hívni, viszont angol tizedespontot használ. Minden programozási nyelvben a törteket ponttal jelöljük. (A vessző értékek felsorolását jelenti!)
2.2 Felsorolt típusok indexelése bővebben
A tömbök indexelése mostanra remélhetőleg tiszta sor már mindenkinek, szépen leülepedett hogy hogyan is történik. Két dolog van amit tudni illik róluk, ezekkel kavarnám fel a mostanra állóvá vált vizet.
2.2.1 A fordított indexelés
Pythonban a növekvő indexelésen kívül létezik egy fordított indexelés, ez értelem szerűen a negatív tartomány felé megy. Mivel olyan szám hogy -0 nem igazán van (most esetünkben), ezért -1 -el indul ez az indexelés. A -1. index mögött a tömb utolsó eleme áll. Egy egyszerű táblázat segít a megértésben:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| p | y | t | h | o | n | b | l | o | g |
| -10 | -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 |
Mivel az index -1-el kezdődött, ezért a tömb első eleme a karakterek száma szorozva -1 -el.
Bátran írjunk negatív számokat is tehát a [] közé, a fenti szabályok szerint működőképes lesz!
2.2.2 Tömb szeletelése (slice)
Pythonban lehetőség van rá, hogy az indexelés során a tömbből egy megadott tartományt "kiemeljünk", ezt hívják slice-olásnak.
Egy string segítségével a legegyszerűbb bemutatni, hogy hogyan is működik a slice. A következő stringben az első 10 számjegy lesz benne, így látszódni fog, hogy az egyes szeleteknél mely elemeket kapjuk majd vissza.
tomb = "0123456789" print tomb[3:] #>>3456789 print tomb[:3] #>>012 print tomb[2:5] #>>234 print tomb[0:5:2] #>>024 print tomb[3::2] #>>3579 print tomb[::2] #>>02468
Az általános formulája a slice-oknak tehát a következő:
Természetesen kombinálhatjuk is őket mint ahogy az a kódrészlet 8. sorában is történik. Ha 0-ás számot akarunk írni, akkor azt elhagyhatjuk. A slice-oknál mindig szerepel kettőspont. Ha mi minden valamilyen szám többszörösein levő értékeket akarjuk kiszedni, akkor mindenképp két kettőspontnak kell szerepelnie a slice-ban. (12. sor egyszerűbben).
2.2.3 Slice trükkök
Megtehetjük hogy minden egy többszörösein levő értékeket vágjuk ki a tömbből. Ilyenkor a tömböt önmagát kapjuk meg. Ez elsőre értelmetlen lehet, viszont a függvényeknél majd így tudjuk lemásolni egy tömb tartalmát.
Ha -1-et írunk a slice-ba, akkor minden -1 többszörösein levő értékeket kapjuk meg, így gyakorlatilag megfordítjuk a tömböt.
tomb = "0123456789" print tomb[::-1] #>>9876543210
3 Zárás
Jó hosszú lett ez a leírás is, de nagy nehezen a végére lehet érni. Az operátorok nélkül nem lehet kezdeni semmit se egyik nyelvvel se, tudni kell a működésüket. Szerintem eléggé kimerítően érintettem őket. Az előző bejegyzésben a tömbök teljes indexelésére nem maradt idő, itt már ez is be lett pótolva. Rövidesen végére érünk a száraz elméletnek, mert tény: itt még túl sok izgalmas dolog nem történt.
A következő bejegyzésben a két legfontosabb függvényről, a kimeneti print-ről, és a bemeneti raw_input-ról lesz szó, azaz szép lassan bevonjuk a felhasználót is a játékba. Az egyes típusok metódusainak leírása függvénykönyvtár ismertető lesz gyakorlatilag, így azok bár különálló postba, de ezen leíráshoz csatolva fognak elkészülni. Előreláthatólag ezt a string-el fogom kezdeni, szerintem ez a legizgalmasabb.
Lendület (?)
Nem kell aggódni, nem eddig tartott a nagy fellángolás, csupán az ünnepek miatt sok más teendőm is akad. Gondolom most pont nem akar tanulni se senki,ezért akkor így kompromisszumosan megegyezhetünk hogy majd jövő évben haladunk tovább :)
-slp
Python változók, típusok [Part1]
A mai bejegyzésben nem feltétlenül a legkellemesebb témát fogom érinteni, ezek a változók és a típusok a Python nyelvben. Egyedül azért nem kellemes, mert ez egy eléggé száraz téma lehet a kezdőknek, de illik tisztában lenni velük. Az alapoknál kezdeném teljesen, szokás szerint.
1. Mik azok a változók? Mik azok a típusok?!
A változók, mint ahogy a matematikában is, olyan dolgok, amik mögött az érték nem konstans, tehát kedvünk szerint bármilyet felvehet. Szoros közelségben állnak a számítástechnikában a változók a típusokkal. Erre az az egyszerű magyarázat, hogy míg nekünk teljesen egyértelmű, hogy egy betű egy számot vagy szöveget takar, addig a számítógépnek ezt a dolgok konkrétan meg kell határozni. Hisz a számítógép memóriájában csak bináris értékek lehetnek, tehát nekünk programozóknak kell definiálnunk, hogy az adott érték mégis mi, szám vagy szöveg, objektum vagy fájl, tehát hogy milyen típusú a változó.
A legtöbb alacsony szintű nyelvben szigorúan definiálva van, hogy egy-egy változóba milyen érték kerülhet, sőt az is, hogy legfeljebb mekkora érték. A C nyelvben egy integer típusú számba például 32bit (2^32-en) méretű egész szám férhet bele. A Pascal nyelvben levő, megegyező nevű integer változók maximális mérete (amikor én tanultam) 16 bit például, ez jóval kisebb.
Arról még szót sem ejtettem, hogy mi a helyzet a tört számokkal. Ezeket külön kell kezelni, C-ben ezek a float-ok és dobule-ök.
Az érdekesebb téma az úgynevezett felsorolt típusok. Ezeket a legtöbb nyelv tömböknek hívja, és ugyan így, a legtöbb nyelvben egy tömbben csak azonos típusú adatok lehetnek.
Ezt a rengeteg megszorítást részben elfelejthetjük, ugyanis a Python eléggé engedékeny a típusokkal. Ebben a nyelvben bármekkora egész számot tárolhatunk integer-ben, és egy tömb minden egyes eleme különböző típusú lehet, ha azt akarjuk.
Mint látjuk, tehát amikor egy értéket akarunk megjegyeztetni a számítógéppel, akkor pontosan meg kell tudnunk mondani neki, hogy milyen típusú lesz az adat majd amire emlékezni kell. Ezért kellenek tehát típusok.
2. Python típusok
Egy kicsit sajátos módszerrel szeretném a típusokat ismertetni. Kezdjük talán azzal, hogy Pythonban valójából -szinte- minden objektum. Hogy mi az az objektum, egyelőre lényegtelen. Csak tartsuk észben.
Ezek után két fő csoportba sorolnám a típusokat. Az egyik a felsorolt, a másik a nem felsorolt típusok. A felsorolt típusok a nem felsorolt típusok felsorolása, értelmes egy magyarázat, de mindjárt teljesen világos lesz.
float - tizedestört
string - karakterlánc
objektum - objektum
dict - szótár (más nyelvekben asszociatív tömb)
tuple - konstans lista (nem változtatható elemeket tartalmazó list)
Ezeket tartom a legfontosabb típusoknak.
3. Változók létrehozása
Most hogy tisztában vagyunk a típusokkal, ideje változókat is létre hozni hozzájuk. Más nyelvekben a változó neve elé szokás kiírni a változó típusát, itt viszont a változó értékadása határozza meg majd a változó típusát. Ez azt is jelenti, hogy egy változó típusa a kódunk során változhat. Ez azért eléggé rugalmas dolog.
Felmerülhet a kérdés, hogy mégis mi a francot tegyünk egy változóba értéknek. Szokás szerint alapértelmezett értéket szokás, azaz 0-t számokba, illetve ürességet stringekbe.
3.1 Változók és nem változók
Változót létrehozni tehát úgy kell, hogy egy egyszerű szövegnek értéket adunk (előjáróban: ezt az = jel fogja tenni). Példával:
valtozo = 0
Ahhoz hogy változót hozzunk létre, annak nevének pár feltételnek meg kell felelnie. Ezek a következők:
A változó neve csak az angol kis- és nagy ABC betűiből állhat, illetve tartalmazhat számokat is, de csak számból nem állhat (az egy érték lenne!). Nem tartalmazhat speciális- és ékezetes karaktereket kivétel a következő karakterek: _
3.2 Az egyes típusok létrehozása
A következő értékek megadásával biztosak lehetünk benne, hogy a nekünk megfelelő típusa lesz a kívánt változóknak:
float : 0.0
string : '' (két szimpla aposztróf vagy két dupla, szóközök nélkül!) ""
list : []
dict : {}
tuple : ()
3.3 Változónak változó
Semmi problémát nem jelent egy változónak egy másik változó értékét átadni. Ehhez csak annyit kell észben tartani, hogy nehogy össze keverjük a változó nevét egy stringgel.
valtozo2 = "masik valtozo" #valtozo2 erteke "masik valtozo" string lesz
valtozo = valtozo2 #valtozo erteke valtozo2 erteke lesz ("masik valtozo")
print valtozo #kiirjuk a valtozo erteket
#>>masik valtozo
(Igaz, mostanra már ne nagyon keverjük össze a stringeket a változó nevekkel. A stringeket '' vagy "" közé írjuk, míg a változó nevét nem)
3.4 "5alma" azaz egyes típusok házasítása AKA típuskonverzió Pythonban
Rendkívül fontos téma a típuskonverzió. Vegyünk egy egyszerű példát: Van két változónk, az egyikben integer, a másikban string. Azt akarjuk, hogy egymás után kiíratásra kerüljenek. Nekünk ez teljesen egyértelmű, viszont a Pythonnak nem. A PHP nyelvnek még ez se jelent problémát, akadnak is gondjai belőle...
Hogy átjárhatóságot biztosítsunk az egyes típusok között, típuskonverziót kell elvégezni rajtuk. Ez egyelőre feleslegesnek tűnhet, de egy fontos dolog: a felhasználótól való bekéréssel string-et fogunk kapni. Ha számot kértünk be akkor is. Egy csomó függvény viszont csak integerekkel képes dolgozni. Ilyenkor mi a teendő? Hát persze hogy típuskonverzió.
3.4.1 "5" nem egyelő 5-el!
Átkozottul fontos lesz azt fejben tartani, hogy mikor mi kerül a képernyőre kiíratásra. Ugyanis a képernyőn nem látjuk, hogy most számot vagy stringet írtunk-e ki, de a program szempontjából ez nagyon is fontos.
Egy stringben gyakorlatilag akármit eltárolhatunk, amit a képernyőn képesek vagyunk megjeleníteni karakter formájában. Azzal, hogy egy stringben számjegyek vannak, azok a számok nem lesznek valódi számok. Ezért hívjuk is őket inkább szám "karaktereknek".
Az egyes típusok között fel lehet állítani egyfajta hierarchiát, azaz hogy melyikben mennyire van megkötve a kezünk. Ez egyszerűen így nézne ki:
Érthető hogy a stringbe aztán akármit bele rakhatunk, hisz mint mondtam, bármi bele kerülhet ami a képernyőn megjeleníthető. Mivel törtet és int-et meg tudunk jeleníteni, így azokból lehet is stringet csinálni.
A float annyival több az integernél, hogy tizedes jegyeket is tartalmaz. Ha ezt elhagyjuk, akkor egy egész számot kapunk, tehát a legalsó elemet, az integert.
Mi akkor a járható út, ha a következő kódot szeretnénk működésre bírni?
integer = 5 string = "alma" print integer+string #Ez a kod igy nem fog lefutni!
Ha ezek után se lenne nyilvánvaló hogy stringből nem lehet integert csinálni, akkor most kiírtam így nyersen az igazságot. Ezek szerint az integerből stringet kell csinálni.
3.6 Típuskonverzió
A következő függvények használatával tudunk egy típust egy másikká alakítani. Fontos, hogy a függvények visszatérési értéke felel meg a kívánt típusnak. A függvények nem módosítják a paraméterül kapott változó típusát!
3.6.1 Nem tudom hogy hogyan kell függvényeket használni!
Jogos a felháborodás, de azok használata még odébb lesz. Előjáróban a következők:
A () karakterek közé kerül az érték (változó is lehet természetesen) aminek a típusát módosítani szeretnénk. Nagyon fontos, hogy a függvény nem fogja változtatni a típusát a változónak, amit paraméternek kapott (a () karakterek között) hanem egy olyan értéket ad vissza, aminek a típusa a kívánt lesz. Azaz egy változónak értékül kell adni a függvényt. Példával:
szoveg = "1337" szam = int(szoveg) print szam #>>1337
3.6.2 Mikor jön el a típuskonverzió ideje?
Én általában akkor végzek típuskonverziót, amikor már végkép muszáj. Azaz ha például egy bekérésnél azonnal szükségem van egy integerre, akkor ott végzem el a konverziót, viszont ha végig számokkal dolgozok, akkor csak azok kiíratásánál alakítom át őket stringekké, persze ha szükséges
3.7. A "3.4.1" megoldása
Mindent szem előtt tartva tudjuk, hogy az integerből kell stringet csinálni, és azt is , hogy bőven elég a kiírásnál elvégezni a típuskonverziót. Akkor a működő kód ezennel:
integer = 5 string = "alma" print str(integer)+string #>>5alma
4. Felsorolt típusok
A felsorolt típusok a "nem felsorolt típusokat" tartalmazzák egymás után. Direkt nem emeltem ki csak a string, float, és integert, ugyanis egy felsorolt típus eleme akár objektum, illetve függvény is lehet, vagy ami tetszik.
4.1 String, tuple, list, dict
"Megengedés" szerint szeretnék haladni, növekvő sorrendbe.
Korábban írtam már, hogy hogyan lehet üres felsorolt típusokat létrehozni. A tartalommal való feltöltés se komplikáltabb, csupán az értékeinket vesszővel elválasztva fel kell sorolni. Szóközt használhatunk a vessző után, ha akarunk. Az utolsó érték után nem kell vessző, de ha ott felejtjük sincs probléma.
4.1.1 String WAT?!
String? Az nem "nem felsorolt típus?" Nos igen, ez az átmenet. Mint azt mondtam, karakterláncnak is szokás hívni. Azaz ez egy olyan tömb, aminek minden egyes eleme pontosan egy darab karakter. Inkább említeném tuple-nek, ugyanis sok más nyelvvel ellentétben a Pythonban nincs rá lehetőség, hogy a string tetszőleges karakterét megváltoztassad egy másikra. A tuple hasonlít így hozzá, hisz ott se lehet változtatni az elemek értékét.
4.2 Hivatkozás az elemekre AKA indexelés (nem irányjelző!)
A felsorolt típusokban az elemeknek indexei vannak, ezek kulcs-érték párok. A string, list, és tuple esetében ezek számok, míg a dict-nél ez egy egyedi azonosító kulcs, amit mi definiálhatunk!
A számozás (minden nyelvben) 0-tól szokott kezdődni. Azaz egy string első karaktere a string 0. indexe. Ezek szerint egy szó utolsó karakterének az indexe a szó hossza minusz egy.
Hivatkozni az értékekre úgy kell, hogy a változó vagy az érték után közvetlenül szögletes zárójelbe rakjuk az indexet. String esetében ez így néz ki:
a = "alma" print a[0] #>>a print "alma"[1] #>>l
4.3 Az igazi felsorolt típusok
Azért a stringben mégis csak minden egyes index mögött egy érték áll. A tuple-ben végre bármely index mögött akármilyen érték lehet, másik tuple, list, akármi is.
Azzal hogy egy index-szel hivatkozunk egy értékre, az olyan, mintha az az érték, amit a hivatkozással megkapunk soha nem lett volna egy felsorolt típusban. Tehát ha kiveszünk egy stringet például egy tuple-ből, akkor a string karaktereit nyugodtan indexelhetjük ha akarjuk. Itt egy kis példa:
tup = (5,"alma") print tup #>>(5, 'alma') print tup[1][0] #>>a
Azaz a tuple 1. indexe (alma) 0. indexe ("a") kerül kiíratásra.
Mint azt írtam, a tuple értékeit nem lehet módosítani! Ezért hasonlít a string nagyon a tuple-re.
4.3.1 List
A list, azaz a lista végre tényleg az, amire szükségünk van. Ugyan úgy történik a hivatkozás benne, mint bárhol máshol, viszont itt megtehetjük azt, hogy a lista egy adott indexén egy értéket módosítunk.
lst = [5,"alma"] print lst #>>[5, 'alma'] lst[0] = 10 print lst #>>[10, 'alma']
Mint látható, a lista 0. indexének az értékéül 10-et adtunk, és gond nélkül azzá is vált.
4.3.2 Dict
Végezetül a dict, dictionary, vagy más nyelvekben asszociatív tömb. Itt az egyes értékekhez tartozó kulcs (ami idáig index volt, és egy egyesével növekvő szám volt 0-tol indulva) egy konstans érték, amit mi definiálunk. Ez aztán akármilyen érték lehet, tört is ha azt akarjuk, teljesen akármi.
A dict felépítése a következő:
dct = {"kulcs":"ertek"}
print dct["kulcs"]
#>>ertek
Rögtön a példában egy (gyakorlatilag) "kulcs" nevű string indexet létre is hoztam, amire hivatkozva az "ertek" stringet kapom vissza.
5 Zárószó
Most már tudjuk, hogy hogyan lehet egyszerű változókat létrehozni, és értékeket pakolni bele. A következő postban a felsorolt típusok módosításáról említenék majd még pár szót és ismertetném az operátorokat is, azaz az alapvető műveleteket amikre a Python képes!
-slp
Karakter összekeverő
Ezzel a posttal felavatnám a "Hirtelen felindulásból elkövetett programozás" nevű kategóriát is. A tag-je "prog_sg" (hasonlóan mint a "főzzünk valamit!" == "programozzunk valamit! :)
Épp ma olvastam facebook-on egy régi szöveget. Remélem ismeritek ti is, beszúrom azért:
"
Egy anlgaii etegyem ktuasátai szenirt nem szimát melyin serenrodbn vnanak a bteűk egy szbóan, az etegyeln ftonos dloog, hogy az eslő és az ultosó bteűk a hölyeükn lneegyek. A tböbi bteű lheet tljees össze-vabisszásagn, mgiés porbléma nlkéül oalvsahtó a szveög. Eennk oka, hogy nem ovalusnk el mniedn bteűt mgaát, hneam a szót eszgébéen.
"
Gondoltam ez nem nagy kunszt Pythonban,így össze is dobtam gyors egy könnyen érthető kódot:
"""
Text shuffler
pythonblog.freeblog.hu AKA slapython.tk
Slapec 2010
"""
import random #random lib szukseges a listak "keveresehez" (shuffle)
text = raw_input("Osszekeverendo szoveg:\n") #bekerjuk a szoveget (ekezetes is lehet!)
text = text.split() #szoveg felbontasa szavakra
result = "" #ebben a valtozoban lesz az eredmeny
for a in text: #"a" erteke mindig egy szo lesz a mondatbol
if len(a) > 3: #ha a szo >3 betus, akkor van ertelme keverni
temp = list(a[1:-1]) #kivagjuk a szo "kozepet", es listava alakitjuk
# mivel string-et nem lehet valtoztatni
random.shuffle(temp) #osszekeverjuk a karaktereket
result += a[0] #kimeneti valtozoba bekerul a szo elso betuje
for b in temp: #a kevert betukon egyesevel vegig haladunk
result += b #minden betut hozzafuzunk a kimeneti valtozohoz
result += a[-1] #az aktualis szo utolso betujet is hozza fuzzuk
else:
result += a #ha a szo <3 karakteres, akkor modositas nelkul
# a kimenethez ragasztjuk
result += " " #minden szo utan visszarakjuk a szokozt
print result #kiirjuk az eredmenyt
Ez a "verzió" nem kezeli a két karakterekből álló betűket (sz, ty, cs, és társai), de hát a kiírásban nem is volt ilyen kikötés :D
Működéséről pár mondat:
A lényeg, hogy a random lib tartalmaz egy shuffle függvényt, ami list-ben levő értékeket tud megkeverni. A string Pythonban is karaktertömb, viszont itt nem lehet a string karaktereit módosítani. Tehát akkor nyilván való két dolog:
1- A string-ből list-et kell csinálni. (hogy meg lehessen a betűket keverni)
2- Ehhez kell egy segédváltozó.
A bekért szöveget egyszerű .split()-el felszeleteljük, így egy list-et kapunk. Mivel ez iterálható, átadtam a for ciklusnak. A for ciklus az "a" változóba mindig a lista aktuális n+1-ik elemét teszi. Tehát elindultunk a mondat szavain.
A feladat kiírás szerint az első és az utolsó karakter fix, tehát az egy, két, és három karakteres szavak biztos nem fognak változni. Ugye 1 karaktert nem értelmes megcserélni, ha kettő lenne akkor azokat meg nem szabad (mert az első és az utolsó fix), és a 3 karakternél a középső 1 szabad karaktert megint nincs értelme cserélgetni.
Tehát ha az aktuális szó hozza háromnál nagyobb, akkor kimetszhetjük a szó közepét. Az első betű a szóból az a[0], az utolsó pedig az a[len(a)-1].
Frissítés: Nagy hülye voltam, a slice-ról szóló bejegyzés közben jöttem rá, hogy egy szó utolsó betűje a -1. indexű, így nem kell a szó hosszát képezni és kivonni belőle. Ez így menőbb :) (A kód módosítva lett!).
Az egész kivágás eredményét list-é alakítjuk. Ez után megkeverjük a list-et.
A kimeneti változóba bekerül az aktuális "a" szó első betűje, azaz az a[0]. Ez után végig megyünk azon a listen, ami a szó közepében levő karaktereket tartalmazza, keverve persze. Minden karaktert egyesével hozzáfűzünk a kimeneti változóhoz. Legutolsó karakternek az a[len(a)-1]-ik betűt is hozzáfűzzük a result-hoz. A végére kerül az eredeti szó utolsó betűje, az a[-1].
Az else ágban semmi trükközés nincs, azt az 1-3 karakter hosszú szót hozzá illesztjük a result-hoz, ahogy van.
A ciklus lefutása az aktuális a szó végét jelöli, így még hozzá kell adni az eredményhez egy szóköz karaktert is. Az egész innentől automatikus fog lenni, és a program a futása végén kiírja az összekevert karakterekből álló mondatot.
Működés közben:![]()
-slp
Python telepítés
Kezdjük is akkor a blogot egy teljesen követendő bejegyzéssel, méghozzá a Python telepítésével. Aki nem gondolt magára amikor szeretteinek karácsonyi ajándékokat vásárolt, az most meglepheti magát egy Python interpreterrel :)
1 - Python beszerzése
A Python értelmezőt a (http://www.python.org/download/) címről tudjuk letölteni. A blogom főként a 2-es verzióval fog foglalkozni, de ejtek majd idővel pár szót a 3-as vonalról is, elvégre azé a jövő. Én egyelőre a 2.7.1-es verziót használom. Aki most kezdi tanulmányait az is ezt a verziót szerezze be lehetőleg. Ha a cikk idő közben elavul, és ebből problémák lennének, akkor azokra majd később keresek megoldást.
Segítségként a kritikus különbségeket kiemelem majd a két verzió közt. Azért előjáróban ne feledjétek: Ha valami nem működik nálad, és 3-as vonalú Pythont használsz, akkor én csak korlátozva tudok benne majd segíteni.
Nos tehát ha Windows rendszerünk van, és nem tudjuk hogy hány bites (nem tudjuk hogy mit jelent az egyáltalán hogy egy rendszer "hány bites") VAGY tudjuk, és a válasz 32, akkor a (Python 2.7.1 Windows installer (Windows binary -- does not include source)) felirat mögötti fájlt töltsük le. 64bites rendszerekhez értelem szerűen 64bites értelmezőt töltsünk.
A linuxos felhasználók többnyire előnyt élveznek a telepítést illetően, ugyanis a legtöbb linux disztribúció tartalmaz előre telepített Python interpretert. Ha valakinek elavult verziója lenne belőle, akkor könnyedén frissítheti a rendszere csomagkezelőjével, vagy letölti a nyelv forráskódját, és lefordítja magának. Nagyon egyszerű művelet ez is, de erre majd egy másik bejegyzés fog kitérni. Nem feltétlen szükséges az interpreter frissítése, alapvető kódok 2.5-ös verzió felett is működnek, de sok függvénykönyvtár változott vagy jött létre ezen verzió óta.
2 - Python telepítése
(A kedvetekért letöröltem a telepített környezetemet csak hogy hiteles leírást közölhessek :)
2.1- A telepítő indítása után eldönthetjük, hogy a Python a rendszerünk minden felhasználója számára elérhető legyen-e. Hagyjuk nyugodtan "Install for all users"-en.
2.2- A telepítési könyvtárat is hagyjuk alapértelmezetten, kivétel ha tudjuk mit csinálunk.
2.3- Ismét, hagyjunk mindent alapbeállításon, kivétel ha tudjuk hogy mit csinálunk.
2.4- El is indul a telepítés. Igazán nem tart sokáig, a végén pedig csak a Finish gomb marad megnyomásra.
3 - Python felavatása
Látjátok, a telepítés nem is volt túl bonyolult. Nem akartam azt írni hogy "Csak nyomkodjátok a Next-et, és kész is" mert akkor túl rövid lenne ez a bejegyzés, de elismerem, tényleg ennyi az egész.
Azzal hogy feltelepült a Python, 4 kiterjesztést ad hozzá a rendszerünkhöz. Ezek a következő kiterjesztések:
Nekünk gyakorlatilag csak a .py fájlokkal kell foglalkoznunk. Már most döntsük el hogy milyen szerkesztővel fogjuk írni a programjainkat.
Ahhoz hogy Python kódot írjunk semmi különleges segédeszközre nincs szükség (kivétel az interpretert persze), csak egy egyszerű szövegszerkesztőre. Az egyszerűt értsük szó szerint, tehát nem Word és ilyenek, jegyzettömb, linuxosoknak nano, teljesen megteszi.
Én részemről a Notepad++ nevű programot preferálom. Ez egy ingyenes szerkesztő, magyar nyelven is tud, illetve ami fontos, hogy színezi a kódunkat. Ez nagyban elősegíti a kód átláthatóságát, bár a Python alapvetően nagyon jól olvasható.
Teljesen feleslegesnek tartom jelenleg bármilyen ennél komplexebb program használatát, hisz az első jó pár postban legfeljebb 1 fájlból álló programokat fogunk írni, fájlban való scrollozáshoz meg nem kell több gigabájt méretű fejlesztői környezet.
3.1 - Hello World!
Írjuk is meg első programunkat, csak hogy leellenőrizzük hogy egészségesen jött-e a világra a Python.
Indítsuk el tehát valamelyik az előző részben felsorolt szövegszerkesztőt, és gépeljük be ezt az egyszerű sort:
print "Hello World!"
Mentsük el a fájlunkat, de ne .txt-nek, hanem .py-nek! Keressük meg a fájlt, és indítsuk el. Egy előugró fekete konzol ablakban egy szempillantásnyi időre megjelentik a szövegünk, de rögtön el is tűnik. Ez azért van, mert a programunk a szöveg kiírása után véget ér, tehát kilép. Ahhoz hogy lássuk hogy mi volt a program utolsó üzenete mielőtt kilépett volna, konzolból indítsuk el a kódunkat!
4 - Python program futtatása jól
Viszonylag egyszerű, leírnám Windowsra és Linuxra is a módszert.
4.1 - Windows
Ha egy már meglevő konzolablakból indítjuk a kódot, akkor a program kilépése nem fogja bezárni a meglevő konzol ablakot. Egyszerűbben szólva tehát konzolból indítjuk a programot.
4.1.1 - Konzolablak Windows alatt
A Futtatás ablakot kell előhalászni ehhez. Start > Futtatás, esetleg Windows 7-en elég a Start menü alján levő kereső mező használata is. Univerzálisabb a következő billentyűkombináció: WINKEY + R. (Winkey: Az a billentyű amire windows logót nyomtattak). A futtatásba írjuk be:
Ezzel előjön egy fekete ablak, hívhatjuk butábban "DOS"nak is, de maradjunk inkább a konzolnál. Itt kénytelenek leszünk elnavigálni magunkat valahogy a munka könyvtárunkba, azaz ahol a .py fájl mentve lett. Ehhez tudni kell a fájl pontos elérési útvonalát. Remélem hogy nem az asztalra mentetted a fájlt, ha igen, akkor sincs persze gond, csak többet kell gépelni.
4.1.2 - Pár DOS parancs
cd [könyvtár] - Könyvtár váltása [könyvtár]-ra
cd .. - Visszalépés egy könyvtárral
dir - Könyvtár tartalmának listázása
Tételezzük fel hogy a D:\ meghajtón van egy python nevű mappánk, és ebben egy hello_world.py. Ez teljesen életszerű szituáció szerintem. A következő a módszer arra hogy eljussunk a fájlhoz:
(Jelmagyarázat: Vastag: konzol írja ; << + aláhúzott : én gépelem be ; egyszerű formázatlan: Python írja ki (eléggé ocsmány megoldás, lesz ez szebb is :))
(Én ezt a sort látom a konzol ablakban)
D:\ <<cd python
D:\Python <<hello_world.py
Hello World!
D:\Python
Ezzel az egyszerű módszerrel mindig látni fogjuk tehát, mint említettem, hogy mit írt ki a programunk mielőtt véget ért volna/kifagyott volna/ismeretlen hibából kifolyólag elhalálozott volna.
4.2 - Linux (úgy általában)
Nem veszek rá mérget, hogy szükséges-e egyáltalán az alábbi pár lépés, de azért a teljesség kedvéért legyen csak itt!
4.2.1 - Konzolablak Linux alatt
Szedjünk elő valamilyen terminálemulátort. Ubuntu-n talán Terminál a neve, vagy Konzol, esetleg Console, de lehet Konsole is. Csomó van.
4.2.2 - Pár terminál parancs
cd .. - Vissza lépés egy könyvtárral
ls - Könyvtár tartalom listázása
Amint elérkeztünk a fájlunkhoz, akkor a következő paranccsal indíthatjuk is:
python [fájlnév]
Az esetemben:
Gyakorlatilag ezzel elindítjuk a Python interpretert, és megnyitandó fájlnak a hello_world.py-t adjuk át neki (paramétere). Linuxon így nincs szükség arra, hogy a Python script futtatható állomány legyen. Ha ilyen formában akarjuk elindítani a kódot:
Akkor a következő dolgokat kell tenni:
4.3 Python kód futtatás Linuxon még jobban
A programunk mindenkori legelső sora a következő legyen:
Így ezek után már ./hello_world.py paranccsal is futtathatjuk a kódunkat.
(Bár erre a bekezdésre nem veszek nektek mérget, ugyanis konkrét próba nélkül írtam. Leellenőrzöm amint tudom)
5 - Zárás
Kérem szépen, elkészült a Python értelmező/interpreter, és le is teszteltük egy gyors Hello World!-el hogy működőképes-e. Elmondtam hogy mi lesz majd a követendő példa a kódok futtatásakor, tehát szerintem ki van kövezve a terep. Hamarosan haladunk tovább, de azért mégis csak karácsony van most.
Boldog Karácsonyt mindenkinek!
-slp
Megj: Valami konkrétabb színezési séma lesz alkalmazva, mert ez így utólag eléggé botrányos.
Megnyitottunk
Hello emberek!
Ezen ünnepélyes postban létrehozottnak nyilvánítanám a Python fejlesztői blogomat. Egyelőre ez a gyári sablon lesz, de majd változik... talán.
Hogy mi lesz itt?
A Python programozási nyelvet szeretném minél jobban terjeszteni. Én is jelenleg sok mindent tanulok még csak belőle, de szeretném a frissen megszerzett tudást azonnal tovább is adni. Teljes referencia fordításokat ne várjatok, mindig csak annyit szeretnék majd magyarázni, ami épp az aktuális dologhoz kell.
Előjáróban 3 fő témát tervezek, az egyik a "fő szál" , úgy is hívhatnánk hogy: Python az alapoktól. Az ilyen postok külön címkét kapnak, így könnyedén lehet majd szelektálni. Az így összegyűlt egyetemes tudást majd egy nagy pdf-ben, könyv szerűen, ingyen, bárki számára elérhetően szeretném majd közzé tenni. Aki megszokta már tőlem az ilyen nagy szavakat, az bizonyára most legyint, úgy se fogok tudni ennyi mindent megcsinálni. Tájékoztatnám a hitetlenkedőket: a könyv sokkal előrébb tart már, mint ez a blog.
Másik fő száll az épp aktuális Python dolgaim fognak lenni. Ide kerülnek majd a saját kódjaim, érdekességek a nyelvről, egy-egy függvénykönyvtár ismertetése, ilyenek. Ezt én csak "Hirtelen felindulásból elkövetett programozás"-nak fogom szólítani, majd lesz szép címkéje is ennek.
A harmadik szál (aminek hiánya csak ezen post megírása után 4 nappal tűnt fel) pedig más programozási dolgok lesznek. Ide kerülhet SQL, AJAX tehát vmi JavaScript, esetleg különböző APIk ismertetése, mint Facebook, Twitter. Egyértelmű, hogy ha valami olyan programot írok, ami ezek valamelyikét használja, akkor egy részletesebb leírásban a fentiek lelkivilágára is kitérek majd.
Egyedül azért nem írom meg az első postot, mert nagyon késő van, de a születés pillanata megtörtént. Remélem lesz akinek hasznára válik a blog. Idő kérdése!
Üdv,
Slapec
Impresszum
Ebben a kis bejegyzésben szeretnék minden programot felsorolni, illetve forrást, és segédeszközt amik a blog fejlődésében segédkeztek.
Szerkesztő:
Slapec
slapec.tk
Felhasznált irodalmak:
Python referencia
http://docs.python.org/reference/
Felhasznált segédeszközök a blogon:
SyntaxHighlighter
http://alexgorbatchev.com/SyntaxHighlighter/
Felhasznált segédeszközök a programozásban:
Python Interpreter
http://www.python.org/
Notepad++
http://notepad-plus-plus.org/
Más programozók szabad kódjai (a teljesség igénye nélkül):
Nincs egyelőre :(
-slp
