2016. március 18., péntek

06 - str, list, dict: az érdekes részek

Az előző részben eljutottunk a vezérlési szerkezetekig, így már gond nélkül írhatunk gondolkodó programot. A nagy rohanásban viszont (szándékosan) elsiklottam jó néhány részlet felett. Ezeket vesszük most át. Akkor GO.

Értékadás rövidebben

Többször láttunk már olyan forráskódot, ahol valami ehhez hasonló szerepelt:

counter = 0
counter = counter + 1

A második sor az érdekes: ha az értékadás operátor bal- és jobb oldalán ugyan az a változó szerepel, akkor azt így rövidíthetjük:

counter = 0
counter += 1

Mindegyik operátornak létezik ilyen rövidített alakja.

Más nyelvekben az érték 1-el való növelésére/csökkentésére használható a ++ vagy -- operátor. Pythonban nincs ilyen.

Ez angolul ezt compound assignment-nek (Python dokumentációban augmented assignment-nek) nevezik. Magyarul sajnos nem találtam rá kifejezést.

Felsorolt típusok

Az str és list felsorolt (sequence) típusai a Python nyelvnek. Amikor egy str típusú értéket írunk le, akkor a Python szemszögéből karakterek felsorolástát végezzük (ezért is hívják néha a stringet karakterláncnak). list esetén expliciten, vesszővel elválasztva írjuk fel tömb elemeit. A felsorolás egyik jelentős tulajdonsága, hogy sorfolytonos, tehát a felsorolás minden elemének eggyel növekvő indexe van. Az első elem indexe a 0, és nem maradnak ki indexek a sorból.

Azt már tudjuk, hogy a felsorolás egy elemét az index operátorral ([ ]) tudjuk elérni, de még egy sor tulajdonság igaz rájuk. Vegyünk párat ezek közül.

Slicing

Az index operátorban nem csak egy értéket, hanem akár egy intervallumot is megadhatunk, így megkapjuk a felsorolás egy szeletét, ez a slicing.

Az intervallum balról zárt, jobbról nyitott.
A slicing során nem törlődnek a kiszeletelt értékek az eredeti felsorolásból, hanem másolatot kapunk róluk.

Itt egy példa hozzá:

a_string = 'Python'
some_chars = a_string[2:4]
print(some_chars)

A 2. sorban látható a varázslat: a [ ] operátor között az intervallum elejét és végét a : karakterrel választjuk el. Ezzel a kifejezéssel a 'Python' string 2-4 indexei közötti szeletet kapjuk meg (a 2. indexű elem (ez a t karakter) már benne lesz a szeletben, de a 4. indexű (az o karakter) már nem.). Így tehát a th értéket veszi fel a some_chars változó. Ez is jelenik meg a képernyőn ha futtatjuk a programot.

A slice legfeljebb 3 értéket tartalmazhat. Nézzük szép sorjában:

  • [start:stop] - Ez volt látható a példában is: a start-tól a stop-ig kapunk értékeket.
  • [start:stop:step] - Opcionálisan a 3. értékben (step) definiálhatjuk, hogy a szeletből csak minden valahányadik értéket akarjuk megkapni.

Léteznek rövidebb formulák, ezek nagyon gyakoriak:

  • [start:] - Ha a kezdő start értéktől a felsorolás végéig kérjük a szeletet, akkor a stop elhagyható. Így szoktak a felsorolás elejéről “levágni” elemeket.
  • [:end] - Hasonlóan, ha a felsorolás elejétől az end értékig kérjük a szeletet, akkor a start elhagyható. Így szoktak a felsorolás végéről “levágni” elemeket.
  • [:] - Ha se start, se stop nincs, akkor értelem szerűen a felsorolás másolatát kapjuk meg, minden elemmel.

A fenti esetek kombinálhatóak a 3. step értékkel is. A lényeg hogy megfelelő számú : maradjon a kifejezésben. Nézzünk egy olyan példát, ahol egy list minden páros indexű elemét akarjuk kigyűjteni:

numbers = [0, 1, 2, 3, 4, 5]
even = numbers[::2]
print(even)

Vagy ahol a páratlan indexűeket:

numbers = [0, 1, 2, 3, 4, 5]
odd = numbers[1::2]
print(odd)

Így az 1. indexű elemet, majd az (1+2) indexűt kapjuk, és így tovább …

Vágjuk le az első és utolsó elemet:

numbers = [0, 1, 2, 3, 4, 5]
print(numbers[1:len(numbers)-1])

Ahhoz hogy ez sikerüljön, a len() függvénnyel le kellett kérni a numbers tömb hosszát. Ha az intervallum eddig tartana, akkor minden értéket megkapnánk a felsorolás végéig, ezért kivontunk 1-et belőle, így az utolsó elemet ki tudtuk hagyni a szeletből. Gondolkodás szempontjából nagyszerű ha erre a műveletre magunktól rájövünk, de Pythonban létezik egy ennél sokkal elegánsabb megoldás is.

Fontos: üres objektumot kapunk akkor, ha olyan intervallumot állapítunk meg, amibe nem esnek elemek a felsorolásból (pl.: az intervallum kezdő indexe nagyobb, mint a felsorolás utolsó indexe).

Negatív indexelés

A felsorolás bal oldalán 0-tól induló, szigorúan monoton 1-el növekvő indexet már sokat láttuk. E mellett használhatunk negatív indexet is, ami a felsorolás jobb oldalán -1-től indul, és szigorúan monoton 1-el csökken. Ez a kis táblázat segít megérteni ezt a párhuzamos dimenziót:

P y t h o n
0 1 2 3 4 5
-6 -5 -4 -3 -2 -1

Az 1. sorban a természetes index látható, amit már jól ismerünk. A 2. sorban az ezzel párhuzamos, negatív indexek olvashatóak. Negatív indexet bármely felsorolt típusnál használhatunk, és slice-ba is rakható.

Akkor vágjuk le a Python szó első és utolsó karakterét:

print('Python'[1:-1])

Olyan kényelmes, már-már a a hihetőség határát súrolja.

Unpacking

Az előző bejegyzésben, a for ciklus kapcsán már találkoztunk az unpacking-el. Ez egy nagyon hasznos művelet a Pythonban, így ugyanis egyetlen utasítással ki tudjuk tenni egy tömb értékeit egy kupac változóba.

Gyakorlatilag, amikor ezt írnánk:

values = [1, 2, 3]
first = values[0]
second = values[1]
third = values[2]

Akkor helyette írhatjuk ezt:

values = [1, 2, 3]
first, second, third = values

Hát ez nem baromi jó?

Természetesen ez ilyen formában csak akkor működik, ha a tömb minden eleméhez tudunk egy változó nevet társítani. Tehát fontos: 3 elemű tömböt 3 változóba kell unpack-elni

Klasszikus probléma amikor meg akarjuk cserélni két változó értékét egymással. Klasszikusan ez csak egy segédváltozó bevezetésével lehetséges:

x = 1
y = 2
print(x, y)

temp = y
y = x
x = temp
print(x, y)

A 5. sorban a temp változóban eltároljuk az y értékét, ugyanis nemsokára felülírjuk ezt a változót, így elveszne az értéke. Az 6. sorban meg is történik a felülírás, itt cserélődik meg az y az x-szel. A 7. sorban az x-nek beállítjuk a temp értékét, ami régi y értéke volt. Így megcseréltünk két változót.

Vagy csinálhatjuk így is:

x = 1
y = 2
print(x, y)

x, y = y, x
print(x, y)

Ahol az 5. sorban az y, x kifejezést kicsomagoljuk az x, y változókba, így meg is cserélődött a két változó értéke

Elem létezésének vizsgálata

Ha arra vagyunk kíváncsiak, hogy egy felsorolás tartalmaz-e egy konkrét értéket, akkor az in operátort kell használnunk.

Írjunk egy programot, ami megmondja, hogy egy konkrét szó szerepel-e egy szövegben:

text = 'Itt Slapec beszél'
slapec_is_speaking = 'Slapec' in text
print(slapec_is_speaking)

A 2. sor az érdekes: a kifejezés True-ra értékelődik ki, mivel a ‘Slapec’ string szerepelt a name változóban, így ez is jelenik meg a konzolban.

Szerintem ez egy nagyon intuitív eszköze a nyelvnek, szinte teljes angol mondatokkal programozunk.

Elem létezésének kizárása

Hasonlóan intuitívan zárhatjuk ki,hogy egy elem létezik-e egy felsorolásban, a not in operátorral.

Írjunk egy programot ami kiírja, hogy az Alice név hiányzik-e a tömbből:

names = ['Bob', 'Carol']
alice_is_missing = 'Alice' not in names
print(alice_is_missing)

Azt hiszem itt már nincs is mit magyarázni.

Természetesen a kizárást úgy is ellenőrizhettük volna, hogy azt a kifejezést írjuk meg, ami azt vizsgálja Alice benne van-e a tömbben, majd a not operátorral ezt tagadjuk, így az ellentétes eseményre kapjuk meg a választ, azaz hogy Alice nincs benne a tömbben. Magyarul: alice_is_missing = not ('Alice' in names), de ilyet nem ír senki.

Elem törlése

A del operátorral törölhető egy index a tömbből.

numbers = [0, 1, 2]
del numbers[0]

print(numbers)

Így a konzolban csak a [1, 2] látszódik majd.

Cikluson belül óvatosan töröljünk az iterált felsorolásból. Iteráció közben változtatni az objektumon nem várt eseményeket okozhat (átugorhatunk elemeket).

Műveletek felsorolásokon

Létezik egy nagyobb marok beépített függvény a Python nyelvben, amivel mindenféle műveletet végezhetünk el felsorolt adatokon. Most ezek következnek, a teljesség igénye nélkül.

A felsorolás hossza

A len() megmondja a neki adott objektum elemeinek számát (hosszát).

long_string = 'Egy hosszú szöveg, sok karakterrel'
long_string_length = len(long_string)
print('Az "', long_string, '" mondat', long_string_length, 'karakterből áll.')

names = ['Alice', 'Bob']
print('Összesen', len(names), 'név szerepel a listában.')

Így a konzolban a Az ” Egy hosszú szöveg, sok karakterrel ” mondat 34 karakterből áll majd az Összesen 2 név szerepel a listában feliratok jelennek meg. Ez azért elég hasznos.

Legkisebb, legnagyobb értékek

A min() és a max() függvények visszaadják a neki adott felsorolás legnagyobb/legkisebb értékeit. Tök ugyan úgy kell használni őket, csak hát más eredményt szolgálnak.

Írjunk egy programot ami megmondja hogy egy diák jegyei közül melyik volt a legjobb és a legrosszabb:

marks = [2, 3, 3, 5, 5, 4]
print('A legjobb jegyed ez volt:', max(marks))
print('A legrosszabb pedig ez:', min(marks))

Felsorolás értékeinek összeadása

A sum() függvénnyel összeadható egy felsorolás összes értéke. Értelem szerűen ez csak akkor lehetséges, ha minden elem numerikus.

Írjunk egy programot ami megmondja egy diák jegyeinek az átlagát:

marks = [2, 3, 3, 5, 5, 4]
average = sum(marks) / len(marks)
print('Az átlagod:', average)

Ehhez ugye össze kell adni minden jegyet s az eredményt el kell osztani a jegyek számával. Összeadni a sum()-al tudunk, a jegyek számát a len() megmondja, kész is a csoda szoftver.

Elemek sorba rendezése

A sorted() függvénnyel növekvő sorrendbe állíthatóak egy tömb elemei

numbers = [4, 5, 1, 3, 2]
numbers_in_order = sorted(numbers)
print(numbers_in_order)

Csökkenőbe rendezéshez a reverse=True argumentumot kell a függvénynek adni:

numbers = [4, 5, 1, 3, 2]
numbers_in_order = sorted(numbers, reverse=True)
print(numbers_in_order)

Ha összetettebb dolgokat, esetleg több szempont szerint szeretnénk sorba rendezni, akkor egy függvényt is átadhatunk a függvénynek, ami eldönti hogy két elem közül melyik a kisebb/nagyobb. Mivel egyelőre még nem tudjuk hogy hogy kell függvényt írni, ezért erre a lehetőségre majd a következő részben fogunk visszatérni.

Felsorolás megfordítása

A reversed() függvénnyel el tudjuk készíteni a felsorolás fordított másolatát. A fenti példák alapján ennek triviálisnak kéne lennie:

numbers = [5, 4, 3, 2, 1]
reversed_numbers = reversed(numbers)
print(reversed_numbers)

Viszont csak a(z egyelőre nem túl sokat mondó) <list_reverseiterator ... felirat fogad minket a konzolban. Ezt azért csinálja velünk a Python hogy memóriát spóroljon. Szerencsére ha a list() függvénynek átadjuk ezt az ismeretlen objektumot, akkor megkapjuk a tömböt:

numbers = [5, 4, 3, 2, 1]
reversed_numbers = list(reversed(numbers))
print(reversed_numbers)

Érdekességként megjegyezném, hogy slice-al is megfordítható a tömb, ha lépésszámnak -1-et állítunk.

numbers = [5, 4, 3, 2, 1]
reversed_numbers = numbers[::-1]
print(reversed_numbers)`

Természetesen az str-ről se feledkezzünk meg:

print('Hello World'[::-1])

A -1 nem kitüntetett érték a slice életében. Tetszőleges negatív számot használhatunk, akkor annyival haladunk visszafelé.

Metódusok

A metódusok (method) témát az előző bejegyzésben súroltuk már, amikor a dict-et kulcs-érték páronként iteráltuk a for ciklusban.

Egy kicsit még jegelném, hogy hogyan is működik ez a varázslat pontosan. Annyit elég tudni dióhéjban, hogy azok a változók amiket idáig megismertünk nem csak magát az adatot tárolják (str típusú a karaktereket, a list a különböző elemeket, vagy az int egy egész számot), hanem az adaton elvégezhető műveleteket is. Ez az egyik olyan tulajdonság, ami miatt minden érték objektum a Python nyelvben.

A metódus hívása nagyon hasonló a függvényéhez, viszont amíg a függvény önmagában szerepel (pl.: len()), és kézzel kell neki átadni az adatot, addig a metódusokat mindig valamilyen kifejezésen keresztül lehet elérni a . (dot operator) segítségével. Attól függ hogy milyen metódusokat érhetünk el, hogy milyen típussal értékelődött ki a kifejezés. Ezen kívül ugyan úgy adhatóak argumentumok egy metódusnak mint egy függvénynek.

A következő bejegyzésben sok példát láthattok erre, mivel rengeteg metódusa van a Python típusainak.

A list metódusai

Elem hozzáadása

A legfontosabb metódus az .append(). Ezzel lehet egy elemet a tömb végéhez hozzáadni.
Írjunk egy programot, amiben egy ciklus elhelyezi egy tömbbe a számokat 0-tól 10-ig:

numbers = []
for i in range(11):
    numbers.append(i)
print(numbers)

Az 1. sorban egy tök üres tömböt hoztunk létre. A 3. sorban látható az új szerkezet.
A numbers változó értéke list, innen tudjuk hogy rendelkezik .append() metódussal. Ez a metódus 1 argumentumot vár, azt az objektumot amit önmagának a végéhez hozzá kell adnia. Azaz a teljes kifejezés azt jelenti, hogy a numbers értékéhez hozzáadjuk (hozzá fűzzük) az i változó értékét. Az utolsó sor futásával látszódik is, hogy szépen feltöltődött a tömb.

Elem beszúrása

Az .insert() metódus tetszőleges indexre beszúr egy elemet. A beszúrt elem helyétől minden elem jobbra tolódik. Használhatunk negatív indexet is, és ha nagyobb indexet állítunk mint ahány elem van a tömbben akkor a tömb végére kerül az új elem.

numbers = [0, 1, 2, 3, 4]
numbers.insert(2, 5)
print(numbers)

A konzolban a [0, 1, 5, 2, 3, 4] sor jelenik meg. Látható hogy az 5 bekerült a 2. indexre, és minden elem jobbra eltolódott tőle.

Ahogy az .append()-el a tömb végére helyeztünk egy elemet, úgy a 0. indexre beszúrással a tömb elejéhez rakhatunk:

numbers = [0, 1, 2, 3, 4]
numbers.insert(0, 5)

print(numbers)

Elemek sorba rendezése, felsorolás megfordítása

Ahogy korábban látható volt, ezt a két funkciót a sorted() és reversed() függvények már megvalósították, azzal a különbséggel, hogy azok mindig az eredeti felsorolás másolatát eredményezték.

Ha másolat készítés nélkül, helyben (in-place) akarunk rendezni vagy megfordítani egy tömböt, akkor használjuk a .sort() vagy .reverse() metódusokat.

random_numbers = [4, 5, 1, 3, 2]
random_numbers.sort()
print(random_numbers)

descending_numbers = [5, 4, 3, 2, 1]
descending_numbers.reverse()
print(descending_numbers)

Mivel ezek a metódusok a tömböt helyben módosítják, ezért a metódus hívása nem eredményez visszatérési értéket.

Az str metódusai

Az str metódusai sohasem változtatják meg a szöveget, hanem a módosított stringgel térnek vissza. Ennek az okára rögtön a következő nagy fejezetben rá is térünk, de addig jöjjön néhány hasznos metódus:

Átalakítás csupa kis/nagybetűssé

A .lower() és .upper() metódusok visszaadják a string csupa kicsi- vagy nagybetűs változatát:

message = 'Hello World'

print(message.lower())
print(message.upper())

Akkor lehet hasznos ez a két metódus ha két string tartalmi egyezőségét akarjuk vizsgálni.

Bár idáig nem említettem, de a Python (ahogy a legtöbb más nyelv is) úgynevezett kis/nagybetű érzékeny (case sensitive) programozási nyelv. Ez azt jelenti, hogy a nyelv különbözőnek tekint két kifejezést akkor, ha a keverjük bennük a kicsi- és nagybetűket.

Így tehát ez a feltétel hamis:

print('Hello World' == 'hello world')

De például a print() helyett se írhatunk Print()-et, mert az már egy másik függvény lenne, a nagy kezdőbetű miatt, ami nem létezik.

Ha gondoskodunk róla hogy két string egyformán csupa kicsi vagy nagybetűs legyen (normalizáljuk a szöveget), akkor az egyezés vizsgálatába nem zavar be többé ez a különbség. Ezt hívják kis/nagybetű érzéketlenségnek (case insensitive).

Írjuk át ennek szellemében a kódot:

print('Hello World'.lower() == 'hello world'.lower())

Mint általában, a normalizálás akkor szükséges, ha a felhasználótól olvasunk be adatot, mert sosem tudják normálisan begépelni azt amire kérjük őket.

Szöveg valamilyen stringgel kezdődik vagy végződik

A startswith() és endswith() metódusok True-val térnek vissza, ha a string a függvénynek adott argumentummal kezdődik vagy végződik.

Írjunk egy programot ami ellenőrzi, hogy egy begépelt fájl kiterjesztése mp3-e:

path = input('Elérési útvonal: ')

if path.endswith('.mp3'):
    print('Ez egy mp3')

Egyébként slice-al is megoldható a probléma, ha a string utolsó 4 karakterét hasonlítjuk, így:

path = input('Elérési útvonal: ')

if path[-4:] == '.mp3':
    print('Ez egy mp3')

Szöveg szétdarabolása

A stringet könnyedén szétdarabolhatjuk egy karaktersor mentén a .split() metódussal. Eredményül egy list-et kapunk.

Akkor lehet hasznos ez a metódus, ha struktúrált adatot (pl.: vesszővel tagoltat) olvasunk be. Ilyenkor egyszerűen szétvágjuk a stringet a határoló karakternél és máris indexekkel hivatkozhatunk a struktúra elemeire.

Mondjuk hogy a felhasználó időjárási megfigyeléseket gépel be, minden értéket vesszővel majd szóközzel elválasztva (jó igényesen). Lezsíroztuk vele, hogy először mindig az észlelés időpontját, majd a hőmérsékletet és végül a csapadékmennyiséget gépeli be. Írjunk erre egy egyszerű programot, ami egyelőre csak egy észlelést olvas be, és rögtön kiírja hogy a begépelt adat szerint mekkora volt a hőmérséklet.

weather = input('Az észlelés (időpont, hőmérséklet, csapadék): ')

date, temperature, rain = weather.split(', ')

print('A hőmérséklet', temperature, 'volt')

A weather változóba bekerül a begépelt szöveg, amit a 3. sorban a .split()-el jól szétvágunk mindenhol ahol a ', ' string előfordult.

Tudjuk (oké, reménykedünk hogy a felhasználó pont megfelelő mennyiségű és formátumú adatot gépelt be), hogy egy 3 elemű tömböt kapunk visszatérési értékül, aminek az elemeit rögtön jó Pythonosan három változóba ki is rakunk. Ez után gyerek játék kiírni a hőmérséklet értékét, azaz a temperature-t.

Szövegek egyesítése

Az előző metódus párja, amikor egy tömbnyi szöveget össze egyesítünk.

Hasonlóan, ez akkor hasznos, ha struktúrált adatot jelenítünk meg. Így akkor írjuk meg az előző program ellentettjét, ami kiírja egy tömb elemeit vesszővel és szóközzel elválasztva:

data = ['2016-01-01 00:00:00', '0°C', '0 mm']

weather = ', '.join(data)

print(weather)

A 3. sorban kiadjuk azt a karaktersorozatot, amivel a tömb elemeit egyesítjük, és a .join() paramétere a felsorolás (most a data tömb).

Kicsit szokatlan lehet ez a sor: miért nem a list-nek van .join() metódusa? Elvégre a tömb elemeit egyesítem egymáshoz. A válasz az, hogy nem csak tömböt, hanem tetszőleges felsorolás elemeit lehet egyesíteni, aminek az eredménye mindig string, így ez az str feladata.

Format string - változók és stringek egyesítése

Azóta akartam erről a témáról beszélni, hogy a beolvasásról és kiírásról szóló bejegyzés zárásában ezt leírtam:

print(a, '+', b, '=', a + b)

Az a baj, hogy ez egy hányás. Olyan sok szóköz, meg minden hieroglifa van benne, hogy le kell ülnöm és végig kell olvasnom hogy értelmezhessem. Szerencsére létezik erre a problémára megoldás (több is!).

A format string egy olyan string amiben kihagyunk helyeket, ahova később változókat illeszthetünk be. Tehát úgy működnek mint egy sablon.

Most az új stílusú format stringet (new-style format string) mutatom be. Ahhoz hogy ezt használjuk egyszerűen helyezzük el a stringben a {} (replacement field) karaktereket oda, ahova egy változó (vagy persze tetszőleges kifejezés értékét) akarjuk írni, és a string .format() metódusbán adjuk át argumentumnként a kifejezést. A többi a Python dolga.

Ilyen egyszerű az egész:

a = 1
b = 2

my_string = '{} + {} = {}'.format(a, b, a+b)
print(my_string)

Igen, ilyenkor jó érzés programozni. A .format() 0. argumentuma az 1. {} helyére kerül, az 1. argumentuma a 2. helyére, és így tovább.

Opcionálisan írhatunk a {} közé sorszámokat, amivel .format()-nak adott argumentum sorszámára hivatkozhatunk. A számozás, ahogy a tömböknél is, 0-tól indul.

a = 1
b = 2

my_string = '{0} + {1} = {2}'.format(a, b, a+b)
print(my_string)

Én inkább ezt a módszert preferálom, így a jövőben tőlem ezt a formát láthatjátok majd. Ezen kívül így megtehetjük azt is, hogy két különböző replacement field ugyan arra az argumentumra hivatkozik (magyarul: kétszer jelenjen meg egy változó a kifejezésben):

a = 1
b = 2

my_string = 'a={0}, b={1}. {0} + {1} = {2}'.format(a, b, a+b)
print(my_string)

A másik dolog, ami már számít is, a float kiírása. Mutatom a problémát:

print('{0}'.format(1/3))

Jó lenne, ha nem lenne tele faltól-falig a konzol a 0.3333333 ...-al. Ezt úgy oldhatjuk meg, ha fix szélességűen jelenítjük meg a törtet, és megadjuk a szélességet, azaz hogy hány tizedesjegy látszódjon. Ezt mind megtehetjük magában a format stringben.

Írjuk ki akkor a törtet 2 tizedesjegyik:

print('{0:.2f}'.format(1/3))

A {0:.2f} felel a kiírásért. Vegyük sorjába a részeit:

  • A 0 azt jelenti, hogy a 0. argumentumot írjuk ki. Ebben semmi új nincs
  • A : azt jelenti hogy az argumentum kiírásakor még elvégzünk valamilyen műveletet
  • A . azt jelenti, hogy a tizedesjegyeket formázzuk
  • A 2 azt, hogy 2 tizedesjegy látszódjon
  • Az f pedig hogy a kifejezés megjelentítése (presentation type) fix szélességű

Végezetül, mivel a format stringben a {} karakterek mindig kicserélésre kerülnek, ezért ha azt akarjuk, hogy mégis jelenjen meg a } vagy a } akkor duplázzuk meg a karaktereket így: }} vagy {{.

A format stringet csak azért nevezzük így, mert tartalmaz replacemenet fieldet. Tehát a format string nem egy spéci str, csak ebben a szerepkörben így szokták nevezni.

A jövőben a } karaktert előszeretettel hívom majd bajuszkának.

dict: az érdekes részek

A dict nem felsorolt típus, mivel a kulcsai nem sorfolytonosak (még akkor se, ha úgy osztjuk ki őket). Ebből kifolyólag nem slice-olhatóak.

A dict kulcsai kicsit saját, külön életet élnek a hozzájuk tartozó értékektől. A feljebb felsorolt összes függvény (len(), min(), max(), sorted(), reversed(), sum()) használható vele, viszont ezek mindig a kulcsokon végzik el a műveletet. Ez leggyakrabban akkor esik rosszul, ha a kulcs-érték párokat sorba akarjuk tenni:

names = {'Alice': 1, 'Bob': 2, 'Carol': 3}

print(sorted(names))

Így bizony egy tömb jelenik meg, amiben a kulcsok vannak felsorolva.

Az operátorok (in, not in és del) szerencsére nem okoznak meglepetést. Az in-el tesztelhetjük, a not in-el kizárhatjuk hogy egy kulcs szerepel-e a dict-ben:

names = {'Alice': 1, 'Bob': 2, 'Carol': 3}
is_alice_here = 'Alice' in names

print(is_alice_here)

Ez pont olyan intuitív mint ahogy megszoktuk.

Törlés esetén a teljes kulcs-érték pár távozik a dict-ből:

names = {'Alice': 1, 'Bob': 2, 'Carol': 3}

del names['Alice']

print(names)

Kulcsok rendezettsége

A fenti néhány sor kimenetén feltűnhet, hogy a dict kulcsai nem olyan sorrendben jelennek meg, mint ahogy azt deklaráltuk a forráskódban. A legrosszabb, hogy előfordul hogy két különböző indítása a programnak két különböző sorrendű dict-et eredményez. Ezzel el is jutottunk a dict legfontosabb tulajdonságához:

A dict kulcsainak sorrendje kiszámíthatatlan.

Azaz nem építhetünk kódot arra, hogy milyen sorrendben követik egymást a dict kulcsai, mivel azok kényük-kedvük szerint változtathatják a helyüket.

Metódusok

A fenti tulajdonságokon kívül a dict-nek is sok metódusa van. Jöjjön pár ezek közül:

Kulcs-érték párok bejárása

Korábban a for ciklusnál láttuk már ezt a metódust, az .items()-et. Ez a metódus egy iterálható objektumot eredményez, ami minden iterációval egy két elemű tömböt ad vissza: az első elem a kulcs, a második az érték. Ezzel a metódussal tehát szépen végig járható az egész dict:

A jobb átláthatóság érdekében most ne foglalkozzunk ciklussal, hanem írjuk ki a metódus visszatérési értékét list-ként:

names = {'Alice': 1, 'Bob': 2, 'Carol': 3}

print(list(names.items()))

A konzolban a [('Alice', 1), ('Bob', 2), ('Carol', 3)] sor jelenik meg. Láthatóak tehát a kis két elemű tömbök.

Kulcsok bejárása

Csak a kulcsokat a .keys() metódussal járhatjuk be:

names = {'Alice': 1, 'Bob': 2, 'Carol': 3}

print(list(names.keys()))

Értékek bejárása

Az értékeket egy nagy összefüggő masszaként járhatjuk be a .values() metódussal:

names = {'Alice': 1, 'Bob': 2, 'Carol': 3}

print(list(names.values()))

dict-ek egyesítése

Az .update() metódussal egyesíthetünk két dict-et.

names = {'Alice': 1}

names.update({'Bob': 2, 'Carol': 3})

print(names)

Ez akkor lehet hasznos, ha több új kulcsot akarunk egyszerre hozzáadni egy meglevő dict-hez. Mivel a dict minden kulcsa egyedi, ezért ha átfedés van a régi- és az új kulcsok között, akkor az újat felülírják a régieket.

Megjegyzés a kulcsok és értékek bejárásáról

Logikus gondolat, hogy például azért akarunk végig járni egy dict-et, hogy minden benne levő értékez növeljünk egy számmal.

Írjunk tehát egy programot amiben a named dict kulcsai nevek, az értékei pedig életkorok. Adjunk minden életkorhoz 1-et:

names = {'Alice': 25, 'Bob': 24, 'Carol': 23}

for age in names.values():
    age += 1

print(names)

Futtassuk a programot és érezzük a csalódottságot: a names dict változatlan maradt. Ennek a miértjét a következő bejegyzésben tárgyaljuk, de a lényeg: azzal hogy az age-t növeljük bár növekszik az érték (írj be az age += 1 alá egy print(age)-t és látni fogod), de az age-hez tartozó érték nem változik a names változóban.

Azért hogy az ilyen problémákat elkerüljük ne az értékeket járjuk be, hanem a kulcsokat, és a kulccsal hivatkozzunk a dict értékére:

names = {'Alice': 25, 'Bob': 24, 'Carol': 23}

for name in names.keys():
    names[name] += 1

print(names)

Zárás

Ez a bejegyzés igen vastagra sikeredett, de ezzel a tudással jó ideig vígan elleszünk. A következő részben függvényt fogunk írni, így ott sokkal kevesebb lesz a magolni való, viszont sokkal többet foglalkozunk a Python lelki világával.

Ez a bejegyzés a Python tutorialom egyik része. Az összes rész listája itt fellelhető.

-slp

Nincsenek megjegyzések:

Megjegyzés küldése