A Python sok natív adattípussal rendelkezik. A fontosak az alábbiak:
A logikai értékek True vagy False értékűek lehetnek.
A számok lehetnek egészek (1 és 2), lebegőpontosak (1.1 és 1.2), törtek (1/2 és 2/3) vagy akár komplex számok is.
A karakterláncok Unicode karakterek sorozatai, például egy html dokumentum.
A bájtok és bájttömbök, például egy jpeg képfájl.
A listák értékek rendezett sorozatai.
A tuple-ök értékek rendezett, nem módosítható sorozatai.
A halmazok értékek rendezetlen kupacai.
A szótárak kulcs-érték párok rendezetlen kupacai.
Természetesen ezeknél több típus van. Pythonban minden objektum, emiatt léteznek modul, függvény, osztály, metódus, fájl és még lefordított kód típusok is. Ezek közül néhányat már láttál: a moduloknak nevük van, a függvényeknek docstring-jeik stb. Az osztályokról az Osztályok és iterátorok fejezetben, a fájlokról pedig a Fájlok fejezetben fogsz többet megtudni.
A karakterláncok és bájtok elég fontosak – és elég bonyolultak – hogy saját fejezetet kapjanak. Előbb nézzük a többit.
⁂
Logikai értékek
Gyakorlatilag bármely kifejezést használhatod logikai kontextusban.
A logikai értékek igazak vagy hamisak lehetnek. A Python két konstanssal rendelkezik, amelyek közvetlenül logikai értékekhez rendelhetők, ezek a True és False. A kifejezések is logikai értékekké értékelődnek ki. Bizonyos helyeken (mint az if utasítások), a Python a kifejezés logikai értékre való kiértékelődését várja. Ezeket a helyeket logikai kontextusoknak nevezzük. Gyakorlatilag bármely kifejezést használhatod logikai kontextusban, és a Python megpróbálja meghatározni annak igazságértékét. A különböző adattípusok különböző szabályokkal rendelkeznek arra vonatkozóan, hogy logikai kontextusban mely értékek számítanak igaznak vagy hamisnak. (A fejezet későbbi konkrét példáinak tükrében majd jobban fogod látni ennek az értelmét.)
Vegyük például ezt a részletet a humansize.py-ból:
if size < 0:
raise ValueError('a szám nem lehet negatív')
A size egy egész, a 0 egy egész, és a < egy numerikus műveleti jel. A size < 0 kifejezés eredménye mindig logikai érték. Ezt te is kipróbálhatod az interaktív Python parancsértelmezőben:
>>> size = 1
>>> size < 0
False
>>> size = 0
>>> size < 0
False
>>> size = -1
>>> size < 0
True
Bizonyos, a Python 2-ből örökölt problémák miatt a logikai értékek számokként kezelhetők. A True 1; a False 0.
>>> True + True
2
>>> True - False
1
>>> True * False
0
>>> True / False
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: int division or modulo by zero
Jaj, jaj, jaj! Ezt ne csináld. Azt is felejtsd el, hogy szóba hoztam.
⁂
Számok
A számok izgalmasak. Olyan sok van belőlük! A Python támogatja mind az egész, mind a lebegőpontos számokat. A megkülönböztetésükhöz nincs típusdeklaráció, a Python a tizedespont jelenléte vagy hiánya alapján tesz különbséget.
>>> type(1) ①
<class 'int'>
>>> isinstance(1, int) ②
True
>>> 1 + 1 ③
2
>>> 1 + 1.0 ④
2.0
>>> type(2.0)
<class 'float'>
A type() függvény használatával ellenőrizheted bármely érték vagy változó típusát. Ahogy az várható, az 1 egy int (egész).
Hasonlóan az isinstance() függvény használatával ellenőrizheted, hogy egy érték vagy változó adott típusú-e.
Egy int és egy másik int összeadásának eredménye is egy int.
Egy int hozzáadása egy float-hoz float (lebegőpontos) típust eredményez. A Python az int értéket float-típusúvá konvertálja az összeadás végrehajtásához, majd eredményként egy float-ot ad vissza.
Egészek lebegőpontossá konvertálása és fordítva
Ahogy az imént láttad, egyes operátorok (mint az összeadás) szükség esetén az egészeket lebegőpontos számokká konvertálja. Ezt a típuskényszerítést te is elvégezheted.
>>> float(2) ①
2.0
>>> int(2.0) ②
2
>>> int(2.5) ③
2
>>> int(-2.5) ④
-2
>>> 1.12345678901234567890 ⑤
1.1234567890123457
>>> type(1000000000000000) ⑥
<class 'int'>
A float() függvény hívásásával kifejezetten float típusúvá konvertálhatsz egy int-et.
Nem meglepő módon, az int() hívásával egy float int-té konvertálható.
Az int() függvény csonkol, nem kerekít.
Az int() függvény a negatív számokat 0 felé csonkolja. Ez egy valódi csonkítás függvény, nem pedig alsó egészrész.
A lebegőpontos számok 15 tizedesjegy pontosságúak.
Az egészek tetszőlegesen nagyok lehetnek.
☞A Python 2 megkülönbözette az int és long egészeket. Az int adattípust a sys.maxint korlátozta, amely platformonként változott, de általában 232-1 volt. A Python 3 csak egy egész típussal rendelkezik, amely nagyrészt úgy viselkedik, mint a Python 2 régi long típusa. A részletekért lásd a pep 237-et.
Általános numerikus műveletek
A számokkal mindenféle dolgokat csinálhatsz.
>>> 11 / 2 ①
5.5
>>> 11 // 2 ②
5
>>> −11 // 2 ③
−6
>>> 11.0 // 2 ④
5.0
>>> 11 ** 2 ⑤
121
>>> 11 % 2 ⑥
1
A / operátor lebegőpontos osztást végez. Akkor is float típusú eredményt ad, ha a számláló és a nevező is int.
A // operátor egy fura egészosztást végez. Ha az eredmény pozitív, akkor veheted úgy, hogy 0 tizdesjegyre csonkít (nem kerekít), de legyél óvatos vele.
Negatív számok egészosztásakor a // operátor „felfelé” kerekít a legközelebbi egészre. Matematikailag nézve „lefelé” kerekít, mivel a −6 kisebb, mint −5, de becsaphat, ha azt várod, hogy −5-re csonkít.
A // operátor nem mindig ad vissza egészet. Ha a számláló vagy a nevező float, akkor is a legközelebbi egészre csonkít, de a tényleges visszatérési érték egy float lesz.
A ** operátor „hatványozást” jelent. 112 = 121.
A % operátor megadja a maradékot egészosztás után. A 11 osztva 2-vel 5, a maradék 1, így az eredmény 1.
☞Python 2-ben a / operátor általában egészosztást jelentett, de egy speciális utasítás beszúrásával rávehető volt lebegőpontos osztásként való viselkedésre is. Python 3-ban a / operátor mindig lebegőpontos osztást jelent. A részletekért lásd a pep 238-at.
Törtek
A Python nem csak egész és lebegőpontos számokat kezel, de el tudja végezni a középiskolában tanult (és azonnal elfelejtett) elegáns műveleteket is.
>>> import fractions ①
>>> x = fractions.Fraction(1, 3) ②
>>> x
Fraction(1, 3)
>>> x * 2 ③
Fraction(2, 3)
>>> fractions.Fraction(6, 4) ④
Fraction(3, 2)
>>> fractions.Fraction(0, 0) ⑤
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "fractions.py", line 96, in __new__
raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
ZeroDivisionError: Fraction(0, 0)
A törtek használatának elkezdéséhez importáld a fractions modult.
Tört definiálásához hozz létre egy Fraction objektumot, és add át a számlálót és nevezőt.
A törtekkel végrehajthatod a szokásos matematikai műveleteket. A műveletek egy új Fraction objektumot adnak vissza. 2 * (1/3) = (2/3)
A Fraction objektum automatikusan egyszerűsíti a törteket. (6/4) = (3/2)
A Pythonnak van annyi esze, hogy ne hozzon létre nulla nevezőjű törteket.
Trigonometria
Pythonban alapszintű trigonometriai számításokat is végezhetsz.
>>> import math
>>> math.pi ①
3.1415926535897931
>>> math.sin(math.pi / 2) ②
1.0
>>> math.tan(math.pi / 4) ③
0.99999999999999989
A math modul rendelkezik egy π konstanssal, amely a kör kerületének és átmérőjének aránya.
A math modul rendelkezik az összes alapszintű trigonometriai függvénnyel, beleértve a sin(), cos(), tan() és ezek változatait, mint az asin().
Ne feledd azonban, hogy a Python pontossága nem végtelen. A tan(π / 4) eredményének 1.0-nak kellene lennie, nem 0.99999999999999989-nek.
Számok logikai kontextusban
A nulla értékek hamisak, a nem nulla értékek igazak.
Logikai kontextusban, például if utasításokban használhatsz számokat. A nulla értékek hamisak, a nem-nulla értékek pedig igazak.
>>> def is_it_true(valami): ①
... if valami:
... print("igen, ez igaz")
... else:
... print("nem, ez hamis") ...
>>> is_it_true(1) ②
igen, ez igaz
>>> is_it_true(-1)
igen, ez igaz
>>> is_it_true(0)
nem, ez hamis
>>> is_it_true(0.1) ③
igen, ez igaz
>>> is_it_true(0.0)
nem, ez hamis
>>> import fractions
>>> is_it_true(fractions.Fraction(1, 2)) ④
igen, ez igaz
>>> is_it_true(fractions.Fraction(0, 1))
nem, ez hamis
Tudtad, hogy az interaktív Python parancsértelmezőben is definiálhatsz saját függvényeket? Csak nyomd meg az ENTER-t az egyes sorok végén, a befejezéshez pedig egy üres sorban nyomd meg az ENTER-t.
Logikai kontextusban a nem nulla egészek igazak, a 0 hamis.
A nem nulla lebegőpontos számok igazak, a 0.0 hamis. Légy óvatos ezzel! Ha a legkisebb kerekítési hiba is előfordul (nem lehetetlen, mint azt az előző szakaszban láttad), akkor a Python a 0.0000000000001-et fogja tesztelni a 0 helyett, és True értéket ad.
Törtek is használhatók logikai kontextusban. A Fraction(0, n) hamis minden n értékre. Minden más tört igaz.
⁂
Listák
A list a Python igásló adattípusa. Amikor azt mondom, „lista”, azt gondolhatod: „olyan tömb, amelyet előre kell deklarálni, csak azonos típusú elemeket tartalmazhat, stb”. Ne gondold. A listák ennél sokkal menőbbek.
☞A lista Pythonban olyan, mint egy tömb Perl 5-ben. Perl 5-ben a tömböket tároló változók a @ karakterrel kezdődnek, Pythonban a változók tetszőleges nevűek lehetnek, és a Python maga tartja nyilván az adattípust.
☞Python listái sokkal többet tudnak, mint a Java tömbök (de úgy is használhatod, ha ez minden vágyad). Jobban hasonlít az ArrayList osztályhoz, amely tetszőleges objektumokat képes tárolni, és dinamikusan tud bővülni az új elemek hozzáadásakor.
Lista létrehozása
A listák létrehozása egyszerű: tedd szögletes zárójelbe az értékek vesszőkkel elválasztott listáját.
>>> a_list = ['a', 'b', 'mpilgrim', 'z', 'example'] ①
>>> a_list
['a', 'b', 'mpilgrim', 'z', 'example']
>>> a_list[0] ②
'a'
>>> a_list[4] ③
'example'
>>> a_list[-1] ④
'example'
>>> a_list[-3] ⑤
'mpilgrim'
Először egy öt elemű listát adsz meg. Ne feledd, hogy ezek megtartják az eredeti sorrendjüket. Ez nem véletlen. A lista elemek rendezett halmaza.
A lista használható nullával kezdődően indexelt tömbként. A nem üres lista első eleme mindig a_list[0].
Ezen öt elemű lista utolsó eleme az a_list[4], mert a listaelemek indexelése mindig nullától kezdődik.
A negatív index az elemeket a lista végétől visszafelé számolva éri el. A nem üres lista utolsó eleme mindig az a_list[-1].
Ha negatív index zavaró, akkor képzeld el így: a_list[-n] == a_list[len(a_list) - n]. Ebben a listában a_list[-3] == a_list[5 - 3] == a_list[2].
Lista szeletelése
Az a_list[0] az a_list első eleme.
Miután definiáltál egy listát, bármely részét megkaphatod új listaként. Ezt a lista szeletelésének nevezzük.
>>> a_list
['a', 'b', 'mpilgrim', 'z', 'example']
>>> a_list[1:3] ①
['b', 'mpilgrim']
>>> a_list[1:-1] ②
['b', 'mpilgrim', 'z']
>>> a_list[0:3] ③
['a', 'b', 'mpilgrim']
>>> a_list[:3] ④
['a', 'b', 'mpilgrim']
>>> a_list[3:] ⑤
['z', 'example']
>>> a_list[:] ⑥
['a', 'b', 'mpilgrim', 'z', 'example']
Egy lista „szeletnek” nevezett bármely részét megkaphatod két index megadásával. A visszatérési érték egy új lista, amely a lista minden elemét tartalmazza az első szeletindextől (ebben az esetben a_list[1]) kezdve a második szeletindexig (ebben az esetben a_list[3]), de azt nem beleértve.
A szeletelés akkor is működik, ha az egyik vagy mindkét szeletindex negatív. Ha ez segít, akkor így is tekintheted: a listát balról jobbra olvasva az első szeletindex megadja az első elemet, amit akarsz, a második szeletindex pedig megadja az első elemet, amit már nem akarsz. A visszatérési érték a kettő között minden.
A listák indexelése nullától kezdődik, így az a_list[0:3] a lista első három elemét adja vissza az a_list[0] elemtől az a_list[3] elemig, de az utóbbit már nem tartalmazza.
Ha a bal oldali szeletindex a 0, akkor kihagyható, és a Python a 0-t feltételezi. Így az a_list[:3] ugyanaz, mint az a_list[0:3] mert a kezdő 0-t a Python feltételezi.
Hasonlóan, ha a jobb oldali szeletindex az utolsó elem indexe, akkor kihagyható. Így az a_list[3:] ugyanaz, mint az a_list[3:5], mert ez a lista öt elemű. Van itt egy kellemes szimmetria. Ebben az öt elemű listában az a_list[:3] visszaadja az első 3 elemet, az a_list[3:] pedig az utolsó két elemet. Tulajdonképpen az a_list[:n] mindig az első n elemet adja vissza, az a_list[n:] pedig a többit, a lista hosszától függetlenül.
Ha mindkét szeletindex kimarad, akkor a lista minden elemét tartalmazni fogja az új lista. De ez nem azonos az eredeti a_list változóval. Ez egy új lista, amely ugyanazokkal az elemekkel rendelkezik. Az a_list[:] a lista teljes lemásolására használható.
Elemek felvétele a listába
Négy módon lehet elemeket felvenni a listába.
>>> a_list = ['a']
>>> a_list = a_list + [2.0, 3] ①
>>> a_list ②
['a', 2.0, 3]
>>> a_list.append(True) ③
>>> a_list
['a', 2.0, 3, True]
>>> a_list.extend(['négy', 'Ω']) ④
>>> a_list
['a', 2.0, 3, True, 'négy', 'Ω']
>>> a_list.insert(0, 'Ω') ⑤
>>> a_list
['Ω', 'a', 2.0, 3, True, 'négy', 'Ω']
A + operátor összefűzi a listákat, ezzel létrehozva egy új listát. Egy lista tetszőleges számú elemet tartalmazhat; nincs méretkorlát (az elérhető memórián kívül). Ha azonban a memóriahasználat fontos szempont, akkor tudnod kell, hogy a listaösszefűzés egy második listát hoz létre a memóriában. Ebben az esetben az új lista azonnal hozzárendelődik a meglévő a_list változóhoz. Így ez a kódsor igazából egy kétlépéses folyamat – összefűzés, majd hozzárendelés – amely (ideiglenesen) rengeteg memóriát tud használni, ha nagy listákkal dolgozol.
Egy lista tetszőleges adattípusú elemeket tartalmazhat, és egy adott lista elemeinek nem kell azonos típusúaknak lenniük. Ez a lista egy karakterláncot, egy lebegőpontos számot és egy egészet tartalmaz.
Az append() metódus egy elemet ad a lista végéhez. (Most már négy különböző adattípus van a listában!)
A listák osztályokként vannak megvalósítva. Egy lista „létrehozása” valójában egy osztály példányosítása. Mint ilyen, a lista rendelkezik rajta műveleteket végző metódusokkal. Az extend() metódus egy argumentumot vár, amely egy lista, és az argumentum minden elemét hozzáfűzi az eredeti listához.
Az insert() metódus egyetlen elemet szúr be a listába. Az első argumentum a lista első olyan elemének indexe, amely ki lesz mozdítva a pozícójából. A lista elemeinek nem kell egyedieknek lenniük, most például két külön 'Ω' értékű elem van a listában: az első elem (a_list[0]) és az utolsó elem (a_list[6]).
☞Az a_list.insert(0, érték) olyan, mint az unshift() függvény Perlben. Felvesz egy elemet a lista elejére, és az összes többi elem indexe megnő eggyel, hogy helyet csináljanak az új elemnek.
Nézzük meg közelebbről az append() és extend() közti különbséget.
>>> a_list = ['a', 'b', 'c']
>>> a_list.extend(['d', 'e', 'f']) ①
>>> a_list
['a', 'b', 'c', 'd', 'e', 'f']
>>> len(a_list) ②
6
>>> a_list[-1]
'f'
>>> a_list.append(['g', 'h', 'i']) ③
>>> a_list
['a', 'b', 'c', 'd', 'e', 'f', ['g', 'h', 'i']]
>>> len(a_list) ④
7
>>> a_list[-1]
['g', 'h', 'i']
Az extend() metódus egyetlen argumentumot vár, amely mindig egy lista, és ennek a listának minden elemét hozzáfűzi az a_list listához.
Ha egy három elemű listával indulsz, és kiterjeszted egy újabb három elemet tartalmazó listával, akkor egy hat elemű listát kapsz.
Másrészt, az append() metódus egyetlen argumentumot vár, amely tetszőleges adattípusú lehet. Itt az append() metódust egy három elemű listával hívod meg.
Ha egy hat elemű listával indulsz, és az append() használatával hozzáfűzöl egy listát, akkor... egy hét elemű listát kapsz. Miért hét? Mert az imént hozzáfűzött utolsó elem maga is egy lista. A listák tetszőleges típusú adatokat tartalmazhatnak, beleértve más listákat is. Lehet, hogy néha ezt akarod, lehet hogy néha nem. De ezt kérted, ezt kapod.
Értékek keresése egy listában
>>> a_list = ['a', 'b', 'új', 'mpilgrim', 'új']
>>> a_list.count('új') ①
2
>>> 'új' in a_list ②
True
>>> 'c' in a_list
False
>>> a_list.index('mpilgrim') ③
3
>>> a_list.index('új') ④
2
>>> a_list.index('c') ⑤
Traceback (innermost last):
File "<interactive input>", line 1, in ?
ValueError: list.index(x): x not in list
Ahogy az várható, a count() metódus visszaadja egy lista adott értékeinek előfordulásainak számát.
Ha csak azt szeretnéd tudni, hogy egy elem szerepel-e a listában vagy sem, akkor az in operátor kicsit gyorsabb, mint a count() metódus. Az in operátor mindig a True vagy False értéket adja vissza, azt nem adja meg, hogy az érték hányszor jelenik meg a listában.
Az in operátor és a count() metódus sem adja meg, hogy az érték hol jelenik meg a listában. Ha szeretnéd tudni, hogy a listában hol van egy érték, akkor használd az index() metódust. A Python alapértelmezésben az egész listában keres, noha megadhatsz egy elhagyható második argumentumot, a keresés kezdő indexét, sőt egy elhagyható harmadik argumentumot is, a keresés befejező indexét (mindkettő nullától kezdődik).
Az index() metódus az érték első előfordulását találja meg a listában. Ebben az esetben az 'új' kétszer fordul elő a listában, az a_list[2] és a_list[4] elemekként, de az index() metódus csak az első előfordulás indexét adja vissza.
Ahogy azt nem várnád, ha az elem nem található meg a listában, akkor az index() metódus kivételt dob.
Várjunk csak? Így van: az index() metódus kivételt dob, ha nem találja az értéket a listában. Ez látványosan eltér a legtöbb nyelvtől, amelyek általában érvénytelen indexet (mint a -1) adnak vissza. Noha ez elsőre idegesítőnek tűnhet, szerintem idővel értékelni fogod. Ez azt jelenti, hogy a programod a probléma forrásánál fog összeomlani, nem pedig később furán és némán. Ne feledd, a -1 egy érvényes listaindex. Ha az index() metódus -1-et adna vissza, az jónéhány, nem túl vidám hibakeresési körhöz vezethetne!
Elemek eltávolítása a listából
A listákban soha nincsenek lyukak.
A listák képesek automatikusan bővülni és szűkülni. A bővülés részt már láttad. Az elemek listából való eltávolítására is számos különböző módszer van.
>>> a_list = ['a', 'b', 'új', 'mpilgrim', 'új']
>>> a_list[1]
'b'
>>> del a_list[1] ①
>>> a_list
['a', 'új', 'mpilgrim', 'új']
>>> a_list[1] ②
'új'
A lista adott elemének törlésére a del utasítás használható.
Az 1-es index elérése az 1-es index törlése után nem eredményez hibát. Minden, a törölt elem utáni elem eltolja a helyzeti indexét az elem törlésével létrejött „lyuk kitöltéséhez”.
Nem ismered a helyzeti indexet? Nem gond, az elemeket érték alapján is eltávolíthatod.
>>> a_list.remove('új') ①
>>> a_list
['a', 'mpilgrim', 'új']
>>> a_list.remove('új') ②
>>> a_list
['a', 'mpilgrim']
>>> a_list.remove('új')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
Egy elemet a listából a remove() metódussal is eltávolíthatsz. A remove() metódus egy értéket vár, és eltávolítja az adott érték első előfordulását a listából. Ismét, a törölt elem mögötti elemek helyzeti indexe csökkenni fog, hogy „kitöltsék a lyukat”. A listákban soha nincsenek lyukak.
A remove() metódust annyiszor hívhatod, ahányszor csak akarod, de kivételt fog dobni, ha a listában nem szereplő értéket próbálsz eltávolítani.
Elemek eltávolítása a listából: tiszteletkör
Egy másik érdekes metódus a pop(). A pop() metódus egy újabb lehetőség elemek eltávolítására egy listából, de van benne egy csavar.
>>> a_list = ['a', 'b', 'új', 'mpilgrim']
>>> a_list.pop() ①
'mpilgrim'
>>> a_list
['a', 'b', 'új']
>>> a_list.pop(1) ②
'b'
>>> a_list
['a', 'új']
>>> a_list.pop()
'új'
>>> a_list.pop()
'a'
>>> a_list.pop() ③
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: pop from empty list
Ha argumentumok nélkül hívod, a pop() listametódus eltávolítja a utolsó elemet a listából, és visszaadja az eltávolított értéket.
A lista tetszőleges elemére is meghívhatod a pop() metódust. Csak adj át egy helyzeti indexet a pop() metódusnak. Ekkor eltávolítja az elemet, az azt követő összes elemet elmozgatja „a lyuk kitöltéséhez”, és visszaadja az eltávolított értéket.
A pop() üres listán való hívásakor kivételt dob.
☞A pop() listametódus argumentumok nélküli hívása olyan, mint a pop() függvény Perlben. Eltávolítja a lista utolsó elemét, és visszaadja az eltávolított elem értékét. A Perl egy másik, shift() nevű függvénnyel is rendelkezik, amely eltávolítja az első elemet, és visszaadja az értékét. Pythonban ez egyenlő az a_list.pop(0) hívással.
Listák logikai kontextusban
Az üres listák hamisak, minden más lista igaz.
Logikai kontextusban, például if utasításokban használhatsz listákat is.
>>> def is_it_true(valami):
... if valami:
... print("igen, ez igaz")
... else:
... print("nem, ez hamis")
...
>>> is_it_true([]) ①
nem, ez hamis
>>> is_it_true(['a']) ②
igen, ez igaz
>>> is_it_true([False]) ③
igen, ez igaz
Logikai kontextusban az üres lista hamis.
Egy legalább egy elemű lista igaz.
Egy legalább egy elemű lista igaz. Az elemek értéke lényegtelen.
⁂
Tuple-ök
A tuple egy megváltoztathatatlan lista. A tuple létrehozása után semmilyen módon sem módosítható.
>>> a_tuple = ("a", "b", "mpilgrim", "z", "példa") ①
>>> a_tuple
('a', 'b', 'mpilgrim', 'z', 'példa')
>>> a_tuple[0] ②
'a'
>>> a_tuple[-1] ③
'példa'
>>> a_tuple[1:3] ④
('b', 'mpilgrim')
A tuple ugyanúgy definiálható, mint egy lista, kivéve hogy az elemek halmaza zárójelek és nem szögletes zárójelek közt van.
Egy tuple elemeinek meghatározott sorrendje van, mint a listák esetén. A tuple indexei nullától kezdődnek, mint a listák esetén, így egy nem üres tuple első eleme mindig a_tuple[0].
A negatív indexek a tuple végétől számítódnak, mint a listák esetén.
A szeletelés is működik, mint a listák esetén. Lista szeletelésekor egy új listát kapsz, tuple szeletelésekor egy új tuple-t.
A tuple és a lista közti legnagyobb különbség, hogy a tuple nem módosítható. Technikai kifejezéssel élve a tuple-ök megváltoztathatatlanok. Gyakorlati szempontból nincsenek olyan metódusaik, amelyek lehetővé tennék a módosításukat. A listák rendelkeznek olyan metódusokkal, mint az append(), extend(), insert(), remove() és pop(). A tuple-ök ezek egyikével sem. A tuple szeletelhető (mert az egy új tuple-t hoz létre), és ellenőrizheted, hogy egy tuple tartalmaz-e egy adott értéket (mert ez nem változtatja meg a tuple-t), és ez minden.
# az előző példa folytatása
>>> a_tuple
('a', 'b', 'mpilgrim', 'z', 'példa')
>>> a_tuple.append("új") ①
Traceback (innermost last):
File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'append'
>>> a_tuple.remove("z") ②
Traceback (innermost last):
File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'remove'
>>> a_tuple.index("példa") ③
4
>>> "z" in a_tuple ④
True
Nem adhatsz elemeket a tuple-höz. A tuple-ök nem rendelkeznek append() vagy extend() metódussal.
Nem távolíthatsz el elemeket egy tuple-ből. A tuple-ök nem rendelkeznek remove() vagy pop() metódussal.
Megkereshetsz elemeket a tuple-ben, mivel ez nem módosítja a tuple-t.
Használhatod az in operátort is egy elem meglétének ellenőrzésére a tuple-ben.
Akkor mire jók a tuple-ök?
A tuple-ök gyorsabbak a listáknál. Ha értékek konstans halmazát definiálod, és a végighaladáson kívül semmit sem akarsz vele csinálni, akkor használj tuple-t a lista helyett.
A kódot biztonságosabbá teszi, ha a nem módosítandó adatokat „írásvédetté” teszed. A tuple használata lista helyett olyan, mintha egy közvetett assert utasításod lenne, amely jelzi, hogy ezek az adatok konstansak, és külön elhatározás (és egy bizonyos függvény) szükséges a felülbírálásához.
Egyes tuple-ök használhatók szótárkulcsokként (konkrétan olyan tuple-ök, amelyek megváltoztathatatlan értékeket, például karakterláncokat, számokat és más tuple-öket tartalmaznak). A listák soha nem használhatók szótárkulcsokként, mivel a listák megváltoztathatók.
☞A tuple-ök átalakíthatók listákká, és fordítva. A beépített tuple() függvény egy listát vár, és visszaad egy tuple-t ugyanazokkal az elemekkel, a list() függvény pedig egy tuple-t vár és listát ad vissza. Gyakorlatilag a tuple() befagyasztja a listát, a list() pedig kiolvasztja.
Tuple-ök logikai kontextusban
Logikai kontextusban, például if utasításokban használhatsz tuple-öket is.
>>> def is_it_true(valami):
... if valami:
... print("igen, ez igaz")
... else:
... print("nem, ez hamis")
...
>>> is_it_true(()) ①
nem, ez hamis
>>> is_it_true(('a', 'b')) ②
igen, ez igaz
>>> is_it_true((False,)) ③
igen, ez igaz
>>> type((False)) ④
<class 'bool'>
>>> type((False,))
<class 'tuple'>
Logikai kontextusban az üres tuple hamis.
Egy legalább egy elemű tuple igaz.
Egy legalább egy elemű tuple igaz. Az elemek értéke lényegtelen. De mit csinál ott az a vessző?
Egy egy elemű tuple létrehozásához vesszőt kell tennie az érték után. A vessző nélkül a Python azt feltételezi, hogy egy extra pár zárójelet tettél ki, ami ártalmatlan, de nem hoz létre tuple-t.
Több érték hozzárendelése egyszerre
Itt egy menő programozási trükk: Pythonban egy tuple használatával egyszerre több értéket is hozzárendelhetsz változókhoz.
>>> v = ('a', 2, True)
>>> (x, y, z) = v ①
>>> x
'a'
>>> y
2
>>> z
True
A v egy három elemű tuple, az (x, y, z) pedig egy három változót tartalmazó tuple. Az egymáshoz rendelésükkel a v értékei a változókhoz lesznek rendelve, a megadott sorrendben.
Ezt sok mindenre fel lehet használni. Tegyük fel, hogy neveket szeretnél rendelni egy értéktartományhoz. A beépített range() függvényt többváltozós értékadásban az egymást követő értékek hozzárendeléséhez használhatod.
>>> (HÉTFŐ, KEDD, SZERDA, CSÜTÖRTÖK, PÉNTEK, SZOMBAT, VASÁRNAP) = range(7) ①
>>> HÉTFŐ ②
0
>>> KEDD
1
>>> VASÁRNAP
6
A beépített range() függvény egészek sorozatát állítja elő. (Technikailag a range() függvény egy iterátort ad vissza, nem pedig listát vagy tuple-t, de erről a különbségről később fogsz tanulni.) A HÉTFŐ, KEDD, SZERDA, CSÜTÖRTÖK, PÉNTEK, SZOMBAT és VASÁRNAP a definiált változók. (Ez a példa a calendar modulból származik, amely naptárakat ír ki, mint a cal nevű unix program. A calendar modul egész konstansokat definiál a hét napjaihoz.)
Most minden változó rendelkezik egy értékkel: a HÉTFŐ a 0, a KEDD 1 és így tovább.
A több érték hozzárendelése használatával olyan függvényeket építhetsz, amelyek több értéket adnak vissza, egyszerűen az értékeket tartalmazó tuple visszaadásával. A hívó ezt kezelheti egyetlen tuple-öként vagy értékeket rendelhet az egyes változókhoz. Sok szabványos Python függvénytár így tesz, beleértve az os modult, amelyet a következő fejezetben fogsz megismerni.
⁂
Halmazok
A halmaz egyedi értékek rendezetlen „kupaca”. Egy halmaz tetszőleges megváltoztathatatlan adattípusú értékeket tartalmazhat. Ha van két halmazod, akkor végrehajthatsz rajtuk általános halmazműveleteket, mint az unió, metszet és különbség.
Halmaz létrehozása
Kezdjük az elején. Egy halmazt létrehozni könnyű.
>>> a_set = {1} ①
>>> a_set
{1}
>>> type(a_set) ②
<class 'set'>
>>> a_set = {1, 2} ③
>>> a_set
{1, 2}
Egy egy értékkel rendelkező halmaz létrehozásához tedd az értéket kapcsos zárójelek ({}) közé.
A halmazok valójában osztályokként vannak megvalósítva, de emiatt most ne aggódj.
Egy több értékkel rendelkező halmaz létrehozásához vesszőkel válaszd el az értékeket, és tedd az egészet kapcsos zárójelek közé.
Halmazt egy listából is létrehozhatsz.
>>> a_list = ['a', 'b', 'mpilgrim', True, False, 42]
>>> a_set = set(a_list) ①
>>> a_set ②
{'a', False, 'b', True, 'mpilgrim', 42}
>>> a_list ③
['a', 'b', 'mpilgrim', True, False, 42]
Egy listából halmaz létrehozásához használd a set() függvényt. (Azok a pedánsok, akik ismerik a halmazok megvalósítását, rá fognak mutatni, hogy ez valójában nem függvényhívás, hanem egy osztály példányosítása. Megígérem, hogy a könyv hátralévő részeiben meg fogod ismerni a különbséget. Egyelőre elég annyi, hogy a set() függvényszerűen működik, és egy halmazt ad vissza.)
Ahogyan azt korábban említettem, egy halmaz tetszőleges adattípusú értékeket tartalmazhat. Ahogyan még korábban említettem, a halmazok rendezetlenek. Ez a halmaz nem emlékszik a létrehozásához használt lista eredeti sorrendjére. Ha elemeket adsz a halmazhoz, az nem fog emlékezni a hozzáadás sorrendjére.
Az eredeti lista változatlan.
Még nincsenek értékeid? Nem gond. Létrehozhatsz üres halmazt is.
>>> a_set = set() ①
>>> a_set ②
set()
>>> type(a_set) ③
<class 'set'>
>>> len(a_set) ④
0
>>> not_sure = {} ⑤
>>> type(not_sure)
<class 'dict'>
Üres halmaz létrehozásához hívd meg a set() függvényt argumentumok nélkül.
Az üres halmaz kiírt ábrázolása egy kicsit furcsán néz ki. Talán ezt vártad: {}? Az egy üres szótárat jelölne, nem pedig egy üres halmazt. A szótárakkal a fejezet későbbi részében fogsz megismerkedni.
A furcsa kiírt ábrázolás ellenére ez egy halmaz…
…és ennek a halmaznak nincsenek elemei.
A Python 2-ből áthozott örökség miatt nem hozhatsz létre üres halmazt két kapcsos zárójellel. Az egy üres szótárat hozna létre, nem pedig egy üres halmazt.
Halmaz módosítása
Meglévő halmazhoz két különböző módon adhatsz értékeket: az add() és az update() metódusokkal.
>>> a_set = {1, 2}
>>> a_set.add(4) ①
>>> a_set
{1, 2, 4}
>>> len(a_set) ②
3
>>> a_set.add(1) ③
>>> a_set
{1, 2, 4}
>>> len(a_set) ④
3
Az add() metódus egyetlen argumentumot vár, amely tetszőleges adattípusú lehet, és az adott értéket hozzáadja a halmazhoz.
Ez a halmaz most 3 elemmel rendelkezik.
A halmazok egyedi értékek kupacai. Ha egy már létező értéket próbálsz hozzáadni, akkor nem történik semmi. Nem fog kivételt dobni, csak nem csinál semmit.
A halmaznak továbbra is 3 eleme van.
>>> a_set = {1, 2, 3}
>>> a_set
{1, 2, 3}
>>> a_set.update({2, 4, 6}) ①
>>> a_set ②
{1, 2, 3, 4, 6}
>>> a_set.update({3, 6, 9}, {1, 2, 3, 5, 8, 13}) ③
>>> a_set
{1, 2, 3, 4, 5, 6, 8, 9, 13}
>>> a_set.update([10, 20, 30]) ④
>>> a_set
{1, 2, 3, 4, 5, 6, 8, 9, 10, 13, 20, 30}
Az update() metódus egy argumentumot vár, egy halmazt, és minden elemét hozzáadja az eredeti halmazhoz. Olyan, mintha a halmaz minden egyes elemére meghívtad volna az add() metódust.
A többször szereplő értékek figyelmen kívül maradnak, mivel a halmazok egy értéket csak egyszer tartalmazhatnak.
Az update() metódust ténylegesen tetszőleges számú argumentummal hívhatod meg. Két halmazzal hívva az update() metódus az egyes halmazok összes elemét hozzáadja az eredeti halmazhoz (a többször szereplő értékek eldobásával).
Az update() metódus számos különböző adattípus objektumait képes kezelni, beleértve a listákat is. Egy listával hívva az update() metódus a lista összes elemét hozzáadja az eredeti halmazhoz.
Elemek eltávolítása a halmazból
Három módon távolíthatsz el egyedi értékeket a halmazból. Az első kettő, a discard() és remove() között egy apró különbség van.
>>> a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
>>> a_set
{1, 3, 36, 6, 10, 45, 15, 21, 28}
>>> a_set.discard(10) ①
>>> a_set
{1, 3, 36, 6, 45, 15, 21, 28}
>>> a_set.discard(10) ②
>>> a_set
{1, 3, 36, 6, 45, 15, 21, 28}
>>> a_set.remove(21) ③
>>> a_set
{1, 3, 36, 6, 45, 15, 28}
>>> a_set.remove(21) ④
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 21
A discard() metódus egyetlen értéket vár argumentumként, és eltávolítja azt az értéket a halmazból.
Ha a discard() metódust a halmazban nem létező értékkel hívod, akkor nem csinál semmi. Nem fog kivételt dobni, csak nem csinál semmit.
A discard() metódus is egyetlen értéket vár argumentumként, és szintén eltávolítja azt az értéket a halmazból.
Itt a különbség: ha az érték nem létezik a halmazban, akkor a remove() metódus egy KeyError kivételt dob.
A listákhoz hasonlóan a halmazoknak is van pop() metódusuk.
>>> a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
>>> a_set.pop() ①
1
>>> a_set.pop()
3
>>> a_set.pop()
36
>>> a_set
{6, 10, 45, 15, 21, 28}
>>> a_set.clear() ②
>>> a_set
set()
>>> a_set.pop() ③
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'pop from an empty set'
A pop() metódus egyetlen értéket távolít el a halmazból, és visszaadja az értéket. Mivel azonban a halmazok rendezetlenek, nincs „utolsó” értékük, így nincs lehetőség az eltávolított érték befolyásolására. Teljesen tetszőleges.
A clear() metódus minden értéket eltávolít a halmazból, és egy üres halmazt hagy. Ez egyenértékű az a_set = set() hívással, amely egy új üres halmazt hoz létre, és az a_set változó korábbi értékét felülírja.
Üres halmazból nem lehet értéket kivenni a pop() metódussal, ez egy KeyError kivételt dob.
Általános halmazműveletek
A Python set típusa számos általános halmazműveletet támogat.
>>> a_set = {2, 4, 5, 9, 12, 21, 30, 51, 76, 127, 195}
>>> 30 in a_set ①
True
>>> 31 in a_set
False
>>> b_set = {1, 2, 3, 5, 6, 8, 9, 12, 15, 17, 18, 21}
>>> a_set.union(b_set) ②
{1, 2, 195, 4, 5, 6, 8, 12, 76, 15, 17, 18, 3, 21, 30, 51, 9, 127}
>>> a_set.intersection(b_set) ③
{9, 2, 12, 5, 21}
>>> a_set.difference(b_set) ④
{195, 4, 76, 51, 30, 127}
>>> a_set.symmetric_difference(b_set) ⑤
{1, 3, 4, 6, 8, 76, 15, 17, 18, 195, 127, 30, 51}
Annak teszteléséhez, hogy egy érték eleme-e egy halmaznak, használd az in operátort. Ez ugyanúgy működik, mint a listák esetén.
Az union() metódus egy új halmazt ad vissza, amely tartalmazza az összes, valamelyik halmazban jelen lévő elemet.
Az intersection() metódus egy új halmazt ad vissza, amely tartalmazza az összes, mindkét halmazban jelen lévő elemet.
A difference() metódus egy új halmazt ad vissza, amely tartalmazza az a_set halmazban jelen lévő, de a b_set halmazban jelen nem lévő elemet.
A symmetric_difference() metódus egy új halmazt ad vissza, amely tartalmazza az összes, pontosan egy halmazban jelen lévő elemet.
Ezen metódusok közül három szimmetrikus.
# az előző példa folytatása
>>> b_set.symmetric_difference(a_set) ①
{3, 1, 195, 4, 6, 8, 76, 15, 17, 18, 51, 30, 127}
>>> b_set.symmetric_difference(a_set) == a_set.symmetric_difference(b_set) ②
True
>>> b_set.union(a_set) == a_set.union(b_set) ③
True
>>> b_set.intersection(a_set) == a_set.intersection(b_set) ④
True
>>> b_set.difference(a_set) == a_set.difference(b_set) ⑤
False
Az a_set szimmetrikus különbsége a b_set-től eltérőnek tűnik a b_set és az a_set szimmetrikus különbségétől, de ne feledd, a halmazok rendezetlenek. Bármely két halmaz, amelyek ugyanazokat az elemeket tartalmazzák (és egyikben sincs több), egyenlőnek számít.
Itt pontosan ez történik. Ne hagyd, hogy a Python Shell becsapjon a halmazok kiírt ábrázolásával. Ugyanazokat az értékeket tartalmazzák, emiatt egyenlők.
Két halmaz uniója is szimmetrikus.
Két halmaz metszete is szimmetrikus.
Két halmaz különbsége nem szimmetrikus. Ennek van értelme, hasonló egy szám kivonásához egy másik számból. Az operandusok sorrendje számít.
Végül néhány, a halmazokkal kapcsolatos kérdés.
>>> a_set = {1, 2, 3}
>>> b_set = {1, 2, 3, 4}
>>> a_set.issubset(b_set) ①
True
>>> b_set.issuperset(a_set) ②
True
>>> a_set.add(5) ③
>>> a_set.issubset(b_set)
False
>>> b_set.issuperset(a_set)
False
Az a_set a b_set részhalmaza – az a_set minden eleme a b_set-nek is eleme.
A kérdést megfordítva a b_set az a_set szülőhalmazaxxx, mert az a_set elemei ab_set-nek is elemei.
Amint hozzáadsz egy olyan értéket az a_set-hez, amely nincs a b_set-ben, mindkét teszt False értéket ad vissza.
Halmazok logikai kontextusban
Logikai kontextusban, például if utasításokban használhatsz halmazokat is.
>>> def is_it_true(valami):
... if valami:
... print("igen, ez igaz")
... else:
... print("nem, ez hamis")
...
>>> is_it_true(set()) ①
nem, ez hamis
>>> is_it_true({'a'}) ②
igen, ez igaz
>>> is_it_true({False}) ③
igen, ez igaz
Logikai kontextusban az üres halmaz hamis.
Egy legalább egy elemű halmaz igaz.
Egy legalább egy elemű halmaz igaz. Az elemek értéke lényegtelen.
⁂
Szótárak
A szótár kulcs-érték párok rendezetlen halmaza. Amikor a szótárhoz hozzáadsz egy kulcsot, akkor a kulcs értékét is meg kell adnod. (Az érték később bármikor módosítható.) A Python szótárak az ismert kulcsú értékek lekérésére vannak optimalizálva, de ez nem működik fordítva.
☞A szótár Pythonban olyan, mint egy hash Perl 5-ben. Perl 5-ben a hasheket tároló változók a % karakterrel kezdődnek. Pythonban a változók tetszőleges nevűek lehetnek, és a Python maga tartja nyilván az adattípust.
Szótár létrehozása
Egy szótárat létrehozni könnyű. A szintaxis hasonló a halmazokhoz, de értékek helyett kulcs-érték párok vannak. A meglévő szótárból az értékeket a kulcsuk alapján keresheted ki.
>>> a_dict = {'kiszolgáló': 'db.diveintopython3.org', 'adatbázis': 'mysql'} ①
>>> a_dict
{'server': 'db.diveintopython3.org', 'database': 'mysql'}
>>> a_dict['kiszolgáló'] ②
'db.diveintopython3.org'
>>> a_dict['adatbázis'] ③
'mysql'
>>> a_dict['db.diveintopython3.org'] ④
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'db.diveintopython3.org'
Első lépésként létrehozol egy két elemű szótárat, és az a_dict változóhoz rendeled. Minden elem egy kulcs-érték pár, és az elemek halmaza kapcsos zárójelek közt van.
A 'kiszolgáló' egy kulcs, és a hozzá tartozó érték, amelyre a_dict['kiszolgáló'] formában hivatkozhatsz, a'db.diveintopython3.org'.
Az 'adatbázis' egy kulcs, és a hozzá tartozó érték, amelyre a_dict['adatbázis'] formában hivatkozhatsz, a'mysql'.
Az értékeket lekérheted kulcs alapján, de a kulcsokat nem kérheted le érték alapján. Emiatt az a_dict['kiszolgáló'] visszatérési értéke'db.diveintopython3.org', de az a_dict['db.diveintopython3.org'] kivételt dob, mert a 'db.diveintopython3.org' nem egy kulcs.
Szótár módosítása
A szótáraknak nincs előre meghatározott méretkorlátjuk. Bármikor hozzáadhatsz új kulcs-érték párokat egy szótárhoz, vagy módosíthatod egy meglévő kulcs értékét. Az előző példa folyatása:
>>> a_dict
{'kiszolgáló': 'db.diveintopython3.org', 'adatbázis': 'mysql'}
>>> a_dict['database'] = 'blog' ①
>>> a_dict
{'kiszolgáló': 'db.diveintopython3.org', 'adatbázis': 'blog'}
>>> a_dict['felhasználó'] = 'mark' ②
>>> a_dict ③
{'kiszolgáló': 'db.diveintopython3.org', 'felhasználó': 'mark', 'adatbázis': 'blog'}
>>> a_dict['felhasználó'] = 'dora' ④
>>> a_dict
{'kiszolgáló': 'db.diveintopython3.org', 'felhasználó': 'dora', 'adatbázis': 'blog'}
>>> a_dict['Felhasználó'] = 'mark' ⑤
>>> a_dict
{'Felhasználó': 'mark', 'kiszolgáló': 'db.diveintopython3.org', 'felhasználó': 'dora', 'adatbázis': 'blog'}
A szótárban nem lehetnek többször szereplő kulcsok. Egy érték meglévő kulcshoz rendelése törli a régi értéket.
Bármikor hozzáadhatsz új kulcs-érték párokat. Ez a szintaxis azonos a meglévő értékek módosításához használttal.
Az új szótárelem (kulcs: 'felhasználó', érték: 'mark') középen jelenik meg. Tulajdonképpen az csak véletlen volt, hogy az első példában az elemek sorrendben jelentek meg, ugyanannyira véletlen, hogy most nem sorrendben jelennek meg.
Egy érték meglévő szótárkulcshoz rendelése egyszerűen lecseréli a régi értéket az újra.
Vajon ez megváltoztatja a felhasználó kulcs értékét "mark"-ra? Nem! Nézd meg jobban a kulcsot – az egy nagy F a "Felhasználó"-ban. A szótárkulcsok megkülönböztetik a kis- és nagybetűket, így ez az utasítás egy új kulcs-érték párt hoz létre, nem pedig egy meglévőt ír felül. Számodra hasonlónak tűnhetnek, de Python szempontjából teljesen különbözők.
Vegyes értékű szótárak
A szótárak nem csupán karakterláncokat tárolhatnak. A szótárértékek tetszőleges adattípusúak lehetnek, beleértve az egész és logikai értékeket, tetszőleges objektumokat vagy akár más szótárakat is. Ezen túl egy szótáron belül az értékeknek nem kell azonos típusúaknak lenniük, szükség szerint keverhetők. A használható szótárkulcstípusok köre korlátozottabb, de a kulcsok lehetnek karakterláncok, egészek és még néhány egyéb típusúak. Egy szótáron belül a kulcsok adattípusai szintén keverhetők.
Tulajdonképpen már láttál egy nem-karakterlánc kulcsokat és értékeket tartalmazó szótárat az első Python programodban.
SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
Szedjük ezt szét az interaktív parancsértelmezőben.
>>> SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
... 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
>>> len(SUFFIXES) ①
2
>>> 1000 in SUFFIXES ②
True
>>> SUFFIXES[1000] ③
['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
>>> SUFFIXES[1024] ④
['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
>>> SUFFIXES[1000][3] ⑤
'TB'
A listákhoz és halmazokhoz hasonlóan a len() függvény megadja a kulcsok számát a szótárban.
A listákhoz és halmazokhoz hasonlóan az in operátorral tesztelheted, hogy egy adott kulcs szerepel-e a szótárban.
Az 1000 egy kulcs a SUFFIXES szótárban, az értéke egy nyolc elemet (pontosabban nyolc karakterláncot) tartalmazó lista.
Hasonlóképpen az 1024 is egy kulcs a SUFFIXES szótárba, az értéke szintén egy nyolc elemet tartalmazó lista.
Mivel a SUFFIXES[1000] egy lista, az elemeit a 0-tól kezdődő indexük alapján elérheted.
Szótárak logikai kontextusban
Az üres szótárak hamisak, minden más szótár igaz.
Logikai kontextusban, például if utasításokban használhatsz szótárakat is.
>>> def is_it_true(valami):
... if valami:
... print("igen, ez igaz")
... else:
... print("nem, ez hamis")
...
>>> is_it_true({}) ①
nem, ez hamis
>>> is_it_true({'a': 1}) ②
igen, ez igaz
Logikai kontextusban az üres szótár hamis.
Egy legalább egy kulcs-érték párt tartalmazó szótár igaz.
⁂
None
A None egy speciális konstans Pythonban. Ez egy null érték. A None nem ugyanaz, mint a False. A None nem 0. A None nem egy üres karakterlánc. A None összehasonlítása bármivel, ami nem None mindig False értéket ad.
A None az egyetlen nullérték. Saját adattípussal rendelkezik (NoneType). A None bármely változóhoz hozzárendelhető, de nem hozhatsz létre más NoneType objektumokat. Minden változó, amely értéke None, egyenlő egymással.
>>> type(None)
<class 'NoneType'>
>>> None == False
False
>>> None == 0
False
>>> None == ''
False
>>> None == None
True
>>> x = None
>>> x == None
True
>>> y = None
>>> x == y
True
None logikai kontextusban
A logikai kontextusokban a None hamis és a not None igaz.
>>> def is_it_true(valami):
... if valami:
... print("igen, ez igaz")
... else:
... print("nem, ez hamis")
...
>>> is_it_true(None)
nem, ez hamis
>>> is_it_true(not None)
igen, ez igaz
Az aktuális munkakönyvtár
Ha kezdő vagy a Pythonban, akkor rengeteg időt fogsz eltölteni a Python Shellben. Ebben a könyvben a következőhöz hasonló példákat fogsz látni:
Importáld az egyik modult az examples mappából
Hívj meg egy függvényt abban a modulban
Magyarázd el az eredményt
Mindig van aktuális munkakönyvtár.
Ha nem ismered az aktuális munkakönyvtárat, akkor az első lépés valószínűleg meghiúsul egy ImportError kivétellel. Miért? Mert a Python a példa modult az importálás keresési útvonalán fogja keresni, de nem fogja megtalálni, mert az examples mappa nem a keresési útvonal egyik könyvtára. Ennek megoldásához a következő két lehetőség egyikét használhatod:
Add az examples mappát az importálás keresési útvonalához
Váltsd át az aktuális munkakönyvtárat az examples mappára
Az aktuális munkakönyvtár egy láthatatlan tulajdonság, amelyet a Python mindig a memóriában tárol. Mindig van aktuális munkakönyvtár, akár a Python Shellben vagy, akár egy Python parancsfájlt futtatsz a parancssorból, akár egy Python cgi parancsfájlt futtatsz egy webkiszolgálón.
Az os modul két függvényt tartalmaz az aktuális munkakönyvtár kezeléséhez.
>>> import os ①
>>> print(os.getcwd()) ②
C:\Python32
>>> os.chdir('/Users/pilgrim/diveintopython3/examples') ③
>>> print(os.getcwd()) ④
C:\Users\pilgrim\diveintopython3\examples
Az os modul a Python része: bármikor, bárhol importálhatod.
Az os.getcwd() függvény használatával lekérdezheted az aktuális munkakönyvtárat. Ha a grafikus Python Shellt futtatod, akkor az aktuális munkakönyvtár az, ahonnan a Python Shell végrehajtható. Windowson ez attól függ, hogy hová telepítetted a Pythont; az alapértelmezett könyvtár a c:\Python32. Ha a Python Shellt a parancssorból futtatod, akkor az aktuális munkakönyvtár az, ahol a python3 elindításakor álltál.
Az os.chdir() függvény használatával megváltoztathatod az aktuális munkakönyvtárat.
Amikor az os.chdir() függvényt meghívtam, akkor egy Linux-stílusú útvonalnevet használtam (osztásjel, meghajtó betűjel nélkül) noha épp Windowst használtam. Ez az egyik olyan hely, ahol a Python megpróbálja eltüntetni az operációs rendszerek közti különbségeket.
Fájlnevek és könyvtárnevek kezelése
Ha már a könyvtáraknál vagyunk, szeretném bemutatni az os.path modult. Az os.path fájlnevek és könyvtárnevek kezelésére szolgáló függvényneveket tartalmaz.
>>> import os
>>> print(os.path.join('/Users/pilgrim/diveintopython3/examples/', 'humansize.py')) ①
/Users/pilgrim/diveintopython3/examples/humansize.py
>>> print(os.path.join('/Users/pilgrim/diveintopython3/examples', 'humansize.py')) ②
/Users/pilgrim/diveintopython3/examples\humansize.py
>>> print(os.path.expanduser('~')) ③
c:\Users\pilgrim
>>> print(os.path.join(os.path.expanduser('~'), 'diveintopython3', 'examples', 'humansize.py')) ④
c:\Users\pilgrim\diveintopython3\examples\humansize.py
Az os.path.join() függvény legalább egy részleges útvonalnévből készít egy útvonalnevet. Ebben az esetben egyszerűen összefűzi a karakterláncokat.
Ebben a kevésbé triviális esetben az os.path.join() függvény egy extra osztásjelet ad az útvonalnévhez a fájlnévhez fűzés előtt. Ez egy fordított törtvonal az osztásjel helyett, mert a példa Windowson készült. Ha ezt a példát Linuxon vagy Mac OS X-en hajtod végre, akkor ehelyett egy osztásjelet kapsz. Ne vacakolj az osztásjelekkel, mindig használd az os.path.join() függvényt, és a Python megoldja.
Az os.path.expanduser() függvény kiterjeszti az aktuális felhasználó saját könyvtárának ábrázolására ~ jelet használó útvonalnevet. Ez minden platformon működik, ahol a felhasználók rendelkeznek saját könyvtárral, beleértve a Linux, Mac OS X és Windows rendszereket. A visszaadott útvonal nem rendelkezik záró osztásjellel, de az os.path.join() függvényt ez nem zavarja.
Ezen módszerek kombinálásával egyszerűen összeállíthatsz a felhasználó saját könyvtárában lévő könyvtárakra és fájlokra mutató útvonalneveket. Az os.path.join() függvény tetszőleges számú argumentumot képes feldolgozni. Rettenetesen megörültem, amikor ezt felfedeztem, mivel az egyik kis hülye függvényem az addSlashIfNecessary(), amelyet mindig meg kell írnom a szerszámos ládám egy új nyelven való kialakításakor. Ne írd meg ezt a kis hülye függvényt Pythonban, ezt a problémát okos emberek már megoldották helyetted.
Az os.path függvényeket tartalmaz a teljes útvonalnevek, könyvtárnevek és fájlnevek felosztására az azokat alkotó részekre.
>>> útvonal = '/Users/pilgrim/diveintopython3/examples/humansize.py'
>>> os.path.split(útvonal) ①
('/Users/pilgrim/diveintopython3/examples', 'humansize.py')
>>> (kvtnév, fájlnév) = os.path.split(útvonal) ②
>>> kvtnév ③
'/Users/pilgrim/diveintopython3/examples'
>>> fájlnév ④
'humansize.py'
>>> (rövidnév, kiterjesztés) = os.path.splitext(fájlnév) ⑤
>>> rövidnév
'humansize'
>>> kiterjesztés
'.py'
A split függvény feloszt egy teljes útvonalnevet, és visszaad egy, az útvonalat és a fájlnevet tartalmazó tuple-t.
Emlékszel, amikor azt mondtam, hogy egy függvényből több érték visszaadására használhatsz többváltozós értékadást? Az os.path.split() függvény pontosan ezt csinálja. A split függvény visszatérési értékét egy két változót tartalmazó tuple-höz rendeli. Minden változó a visszaadott tuple megfelelő elemének értékét kapja.
Az első változó, a kvtnév megkapja az os.path.split() által visszaadott tuple első elemének értékét, a fájl útvonalát.
A második változó, a fájlnév megkapja az os.path.split() által visszaadott tuple második elemének értékét, a fájl nevét.
Az os.path tartalmazza az os.path.splitext() függvényt is, amely feloszt egy fájlnevet, és visszaadja a fájlnevet és a fájlkiterjesztést tartalmazó tuple-t. Ugyanezzel a módszerrel ezeket is önálló változókhoz rendelheted.
Könyvtárak felsorolása
A glob modul a szabványos Python függvénytár másik eszköze. Egyszerű módszert nyújt egy könyvtár tartalmának lekérésére a programodból, és a parancssor használata során már remélhetőleg megszokott helyettesítő karaktereket használja.
A glob modul shell-szerű helyettesítő karaktereket használ.
>>> os.chdir('/Users/pilgrim/diveintopython3/')
>>> import glob
>>> glob.glob('examples/*.xml') ①
['examples\\feed-broken.xml',
'examples\\feed-ns0.xml',
'examples\\feed.xml']
>>> os.chdir('examples/') ②
>>> glob.glob('*test*.py') ③
['alphameticstest.py',
'pluraltest1.py',
'pluraltest2.py',
'pluraltest3.py',
'pluraltest4.py',
'pluraltest5.py',
'pluraltest6.py',
'romantest1.py',
'romantest10.py',
'romantest2.py',
'romantest3.py',
'romantest4.py',
'romantest5.py',
'romantest6.py',
'romantest7.py',
'romantest8.py',
'romantest9.py']
A glob modul egy helyettesítő karaktert tartalmazó karakterláncot vár, és visszaadja az arra illeszkedő összes fájl és könyvtár elérési útját. Ebben a példában a karakterlánc a könyvtár elérési útja, plusz a „*.xml”, amely az examples alkönyvtárban lévő összes .xml fájlra illeszkedik.
Most váltsd át az aktuális munkakönyvtárat az examples alkönyvtárra. Az os.chdir() függvény elfogad relatív útvonalneveket is.
A glob mintába több helyettesítő karakter is felvehető. Ez a példa megtalálja az aktuális munkakönyvtár összes .py kiterjesztésű, és atest szót a fájlnevükben bárhol tartalmazó fájlját.
Fájlmetaadatok lekérése
Minden korszerű fájlrendszer tárol metaadatokat a fájlokról: létrehozás dátuma, legutóbbi módosítás dátuma, fájlméret és így tovább. A Python egységes api-t biztosít ezen metaadatok eléréséhez. A fájlt nem kell megnyitnod, csak a fájlnévre van szükséged.
>>> import os
>>> print(os.getcwd()) ①
c:\Users\pilgrim\diveintopython3\examples
>>> metadata = os.stat('feed.xml') ②
>>> metadata.st_mtime ③
1247520344.9537716
>>> import time ④
>>> time.localtime(metadata.st_mtime) ⑤
time.struct_time(tm_year=2009, tm_mon=7, tm_mday=13, tm_hour=17,
tm_min=25, tm_sec=44, tm_wday=0, tm_yday=194, tm_isdst=1)
Az aktuális munkakönyvtár az examples mappa.
A feed.xml egy fájl az examples mappában. Az os.stat() függvény hívása egy olyan objektumot ad vissza, amely számos különböző típusú metaadatot tartalmaz a fájlról.
Az st_mtime a módosítás ideje, de egy nem különösebben hasznos formátumban van. (Technikailag ez az Epoch, azaz 1970. január 1. első másodperce óta eltelt másodpercek száma. Komolyan.)
A time modul a szabványos Python függvénytár része. A különböző időábrázolások közti átalakításhoz, az időértékek karakterláncokká formázásához és az időzónák kezeléséhez használható függvényeket tartalmaz.
A time.localtime() függvény átalakít egy időértéket az Epoch-óta-eltelt-másodpercek formátumról (ezt az os.stat() függvény által visszaadott st_mtime tulajdonság tartalmazza) a sokkal hasznosabb év, hónap, nap, óra, perc, másodperc stb. formátumra. Ezt a fájlt utoljára 2009. július 13-án, délután 5.25 körül módosították utoljára.
# az előző példa folytatása
>>> metadata.st_size ①
3070
>>> import humansize
>>> humansize.approximate_size(metadata.st_size) ②
'3.0 KiB'
Az os.stat() függvény visszaadja a fájl méretét is, az st_size tulajdonságban. A feed.xml fájl 3070 bájt.
Az st_size tulajdonságot átadhatod az approximate_size() függvénynek.
Abszolút útvonalnevek összeállítása
Az előző szakaszban a glob.glob() függvény relatív útvonalnevek listáját adta vissza. Az első példa olyan útvonalneveket tartalmaz, mint az 'examples\feed.xml', és a második példa még rövidebbeket, mint a 'romantest1.py'. Amíg ugyanabban az aktuális munkakönyvtárban maradsz, ezek a relatív útvonalnevek működni fognak a fájlok megnyitásakor vagy a metaadatok lekérésekor. Ha azonban abszolút – azaz a könyvtárneveket egészen a gyökérkönyvtárig vagy a meghajtó betűjelig tartalmazó – útvonalnevet szeretnél összeállítani, akkor az os.path.realpath() függvényt kell használnod.
>>> import os
>>> print(os.getcwd())
c:\Users\pilgrim\diveintopython3\examples
>>> print(os.path.realpath('feed.xml'))
c:\Users\pilgrim\diveintopython3\examples\feed.xml
⁂
Listafeldolgozók
Listafeldolgozóban tetszőleges Python kifejezést használhatsz.
A listafeldolgozó használatával tömören le lehet képezni egy listát egy másik listára egy függvény alkalmazásával a lista minden egyes elemére.
>>> a_list = [1, 9, 8, 4]
>>> [elem * 2 for elem in a_list] ①
[2, 18, 16, 8]
>>> a_list ②
[1, 9, 8, 4]
>>> a_list = [elem * 2 for elem in a_list] ③
>>> a_list
[2, 18, 16, 8]
Az értelmezéshez nézd jobbról balra. Az a_list a leképezett lista. A Python parancsértelmező elemenként végigjárja az a_list listát, az egyes elemek értékét pedig ideiglenesen az elem változóhoz rendeli. A Python ezután alkalmazza az elem * 2 függvényt, és az eredményt hozzáfűzi a visszaadott listához.
A listafeldolgozó új listát hoz létre; az eredetit nem módosítja.
A listafeldolgozó eredményét biztonságos hozzárendelni a leképezett változóhoz. A Python az új listát a memóriában állítja össze, és amikor a listafeldolgozó kész, akkor rendeli az eredményt az eredeti változóhoz.
Listafeldolgozóban tetszőleges Python kifejezést használhatsz, beleértve az os modul fájlok és könyvtárak kezelésére szolgáló függvényeit is.
>>> import os, glob
>>> glob.glob('*.xml') ①
['feed-broken.xml', 'feed-ns0.xml', 'feed.xml']
>>> [os.path.realpath(f) for f in glob.glob('*.xml')] ②
['c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-broken.xml',
'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-ns0.xml',
'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed.xml']
Ez visszaadja az aktuális munkakönyvtárban lévő összes .xml fájl listáját.
Ez a listafeldolgozó veszi az .xml fájlok előbbi listáját, és átalakítja azt teljes útvonalnevek listájává.
A listafeldolgozók szűrni is tudják az elemeket, ezáltal az eredeti listánál rövidebb eredményt előállítva.
>>> import os, glob
>>> [f for f in glob.glob('*.py') if os.stat(f).st_size > 6000] ①
['pluraltest6.py',
'romantest10.py',
'romantest6.py',
'romantest7.py',
'romantest8.py',
'romantest9.py']
Egy lista szűréséhez felvehetsz egy if kifejezést a listafeldolgozó végére. Az if kulcsszó utáni kifejezés kiértékelődik a lista minden egyes elemére. Ha a kifejezés True-ra értékelődik ki, akkor a kimenet tartalmazni fogja az elemet. Ez a listafeldolgozó megnézi az aktuális könyvtár összes .py fájlját, és az if kifejezés annak alapján szűri ezt a listát, hogy az egyes fájlok mérete nagyobb-e 6000 bájtnál. Hat ilyen fájl van, így a listafeldolgozó hat fájlnévből álló listát ad vissza.
A listafeldolgozók az eddigi példákban egyszerű kifejezéseket mutattak be – egy szám konstanssal szorzása, egy függvény hívása, vagy egyszerűen az eredeti listaelem visszaadása (szűrés után). De a listafeldolgozó összetettségének nincs korlátja.
>>> import os, glob
>>> [(os.stat(f).st_size, os.path.realpath(f)) for f in glob.glob('*.xml')] ①
[(3074, 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-broken.xml'),
(3386, 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-ns0.xml'),
(3070, 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed.xml')]
>>> import humansize
>>> [(humansize.approximate_size(os.stat(f).st_size), f) for f in glob.glob('*.xml')] ②
[('3.0 KiB', 'feed-broken.xml'),
('3.3 KiB', 'feed-ns0.xml'),
('3.0 KiB', 'feed.xml')]
Ez a listafeldolgozó megtalálja az összes .xml fájlt az aktuális munkakönyvtárban, lekéri az egyes fájlok méretét (az os.stat() függvény hívásával), és összeállít egy tuple-t a fájlok méretéből és az abszolút elérési útjukból (az os.path.realpath() függvény hívásával).
Ez a feldolgozó az előzőre épít, és meghívja az approximate_size() függvényt az egyes .xml fájlok fájlméretével.
⁂
Szótárfeldolgozók
A szótárfeldolgozó olyan, mint a listafeldolgozó, csak lista helyett szótárat állít elő.
>>> import os, glob
>>> metadata = [(f, os.stat(f)) for f in glob.glob('*test*.py')] ①
>>> metadata[0] ②
('alphameticstest.py', nt.stat_result(st_mode=33206, st_ino=0, st_dev=0,
st_nlink=0, st_uid=0, st_gid=0, st_size=2509, st_atime=1247520344,
st_mtime=1247520344, st_ctime=1247520344))
>>> metadata_dict = {f:os.stat(f) for f in glob.glob('*test*.py')} ③
>>> type(metadata_dict) ④
<class 'dict'>
>>> list(metadata_dict.keys()) ⑤
['romantest8.py', 'pluraltest1.py', 'pluraltest2.py', 'pluraltest5.py',
'pluraltest6.py', 'romantest7.py', 'romantest10.py', 'romantest4.py',
'romantest9.py', 'pluraltest3.py', 'romantest1.py', 'romantest2.py',
'romantest3.py', 'romantest5.py', 'romantest6.py', 'alphameticstest.py',
'pluraltest4.py']
>>> metadata_dict['alphameticstest.py'].st_size ⑥
2509
Ez még nem szótárfeldolgozó, hanem egy listafeldolgozó. Megtalálja az összes .py fájlt, amelyek neve tartalmazza a test szót, majd összeállít egy tuple-t a fájlok neveiből és metaadataiból (amelyeket az os.stat() függvény ad vissza).
Az eredményül kapott lista minden eleme egy tuple.
Ez egy szótárfeldolgozó. A szintaxis hasonló a listafeldolgozókéhoz, két különbséggel. Először is, kapcsos és nem szögletes zárójelek közt áll. Másodszor, minden elemhez egy kifejezés helyett két, kettősponttal elválsztott kifejezést tartalmaz. A kettőspont előtti kifejezés (ebben a példában az f) a szótár kulcsa, a kettőspont utáni kifejezés (ebben a példában az os.stat(f)) az érték.
A szótárfeldolgozó szótárat ad vissza.
Ennek a szótárnak a kulcsai a glob.glob('*test*.py') által visszaadott fájlnevek.
Az egyes kulcsokhoz tartozó érték az os.stat() függvény visszatérési értéke. Ez azt jelenti, hogy egy fájl a neve alapján „kikereshető” a szótárból a metaadatainak eléréséhez. Az egyik ilyen metaadat az st_size, a fájlméret. Az alphameticstest.py fájl 2509 bájt hosszú.
A listafeldolgozóhoz hasonlóan a szótárfeldolgozóhoz hozzáadhatsz egy if kifejezést a bemeneti sorozat szűréséhez egy minden elemmel együtt kiértékelésre kerülő kifejezéssel.
>>> import os, glob, humansize
>>> metadata_dict = {f:os.stat(f) for f in glob.glob('*')} ①
>>> humansize_dict = {os.path.splitext(f)[0]:humansize.approximate_size(meta.st_size) \
... for f, meta in metadata_dict.items() if meta.st_size > 6000} ②
>>> list(humansize_dict.keys()) ③
['romantest9', 'romantest8', 'romantest7', 'romantest6', 'romantest10', 'pluraltest6']
>>> humansize_dict['romantest9'] ④
'6.5 KiB'
Ez a szótárfeldolgozó létrehozza az aktuális munkakönyvtár fájljainak listáját, (glob.glob('*')), lekéri az egyes fájlok metaadatait (os.stat(f)), és összeállít egy szótárat, amelynek kulcsai a fájlnevek, értékei pedig az egyes fájlok metaadatai.
Ez a szótárfeldolgozó az előző feldolgozóra épül, kiválogatja a 6000 bájtnál nagyobb fájlokat (if meta.st_size > 6000), és a kapott listát használja egy szótár összeállítására, amelynek kulcsai a kiterjesztés nélküli fájlnevek (os.path.splitext(f)[0]), értékei pedig az egyes fájlok becsült méretei (humansize.approximate_size(meta.st_size)).
Ahogyan az előző példában láthattad, hat ilyen fájl van, emiatt ebben a szótárban is hat elem van.
Az egyes kulcsok értéke az approximate_size() függvény által visszaadott karakterlánc.
További menő dolgok szótárfeldolgozókkal
Itt egy szótárfeldolgozókkal kapcsolatos trükk, ami egyszer még jól jöhet: egy szótár kulcsainak és értékeinek felcserélése.
>>> a_dict = {'a': 1, 'b': 2, 'c': 3}
>>> {érték:kulcs for kulcs, érték in a_dict.items()}
{1: 'a', 2: 'b', 3: 'c'}
Természetesen ez csak akkor működik, ha a szótár értékei megváltoztathatatlanok, például karakterláncok vagy tuple-k. Ha ezt egy listákat tartalmazó szótárral próbálod meg, az látványosan nem fog sikerülni.
>>> a_dict = {'a': [1, 2, 3], 'b': 4, 'c': 5}
>>> {érték:kulcs for kulcs, érték in a_dict.items()}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <dictcomp>
TypeError: unhashable type: 'list'
⁂
Halmazfeldolgozók
Ne hagyjuk ki a halmazokat se, ezeknek is van saját feldolgozószintaxisuk, amely nagyon hasonlít a szótárfeldolgozók szintaxisához. Az egyetlen különbség, hogy a halmazok csak értékeket tartalmaznak kulcs:érték párok helyett.
>>> a_set = set(range(10))
>>> a_set
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> {x ** 2 for x in a_set} ①
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}
>>> {x for x in a_set if x % 2 == 0} ②
{0, 8, 2, 4, 6}
>>> {2**x for x in range(10)} ③
{32, 1, 2, 4, 8, 64, 128, 256, 16, 512}
A halmazfeldolgozók bemenete lehet egy halmaz. Ez a halmazfeldolgozó kiszámítja a 0 és 9 közti számok négyzetét.
A listafeldolgozókhoz és szótárfeldolgozókhoz hasonlóan a halmazfeldolgozók is tartalmazhatnak egy if kifejezést az egyes elemek szűréséhez azok visszaadása előtt az eredmény halmazba.
A halmazfeldolgozók bemenete nem csak halmaz lehet, tetszőleges sorozatot is elfogadnak.
Nincsenek megjegyzések:
Megjegyzés küldése