Tartalom: Dict, tuple, kivételkezelés (try / except)
Eddig ha egy objektumban több más objektum felsorolását akartuk tárolni, akkor listákat használtunk:
[1, 2, 3, 4]
[1, 2, 3, 4]
["Bill", "Charlie", "Percy"]
['Bill', 'Charlie', 'Percy']
Általában akkor szokás a lista típust használni, ha az adatunk valóban lista-szerű: nem fontos, hogy pontosan hány eleme van, és egy-egy elem funkcióját nem az határozza meg, hogy ő hányadik a listában. Ilyen volt a filmes táblázatunk.
import os
film_fajl = os.path.join("data", "movies.tsv")
filmek = [line.strip().split('\t') for line in open(film_fajl)]
filmek[:3]
[['Toy Story ', '1995', 'animation,children,comedy'], ['GoldenEye ', '1995', 'action,adventure,thriller'], ['Four Rooms ', '1995', 'thriller']]
Amikor azonban egy ilyen felsorolásnak van egy kötött szerkezete, mint például a filmes táblázat egyes sorainál, ahol fontos, hogy a felsorolás pontosan 3 hosszúságú, és minden pozíciónak szerepe van, akkor a lista helyett egy másik típust szoktunk használni, a tuple-t, vagy más néven rendezett n-es véges listát:
filmek[0]
['Toy Story ', '1995', 'animation,children,comedy']
tuple(filmek[0])
('Toy Story ', '1995', 'animation,children,comedy')
list("pingvin")
['p', 'i', 'n', 'g', 'v', 'i', 'n']
A tuple elemeit sima kerek zárójelek közé írjuk. Egy-egy elemét ugyanúgy tudjuk lekérdezni, mint egy listáét:
film = tuple(filmek[0])
film
('Toy Story ', '1995', 'animation,children,comedy')
film[0]
'Toy Story '
Viszont egy tuple-höz nem fűzhetünk hozzá újabb elemet:
film.append("x")
AttributeError: 'tuple' object has no attribute 'append'
És nem változtathatjuk meg 1-1 elemét:
film[1] = "1994"
TypeError: 'tuple' object does not support item assignment
Cserébe több előnye is van a tuple-öknek a listákkal szemben, ezek egy részéről még fogunk tanulni, de egyelőre annyi elég, hogy "tisztább helyzetet" teremt a kódunkban: ha egy felsorolásunknak fix szerkezete van, ha nem dolga, hogy változzon a hosszúsága vagy hogy változzanak az elemei, akkor tuple-ban tároljuk, így nem fogjuk még tévedésből sem "elrontani" a benne lévő adatot:
filmek = [tuple(film) for film in filmek]
filmek[:3]
[('Toy Story ', '1995', 'animation,children,comedy'), ('GoldenEye ', '1995', 'action,adventure,thriller'), ('Four Rooms ', '1995', 'thriller')]
Az eddig megismert típusok (int, float, string, lista, tuple) számokat vagy szövegeket illetve ezek felsorolásait tárolta. Most egy olyan típust ismerünk meg, ami egy leképezést vagy megfeleltetést tárol különböző objektumok párjai között, ez a típus a dictionary (magyarul: szótár).
Nevével ellentétben a dictionary nem definíciókat tárol, hanem egyszerűen a benne tárolt objektumok mindegyikét hozzákapcsolja valamilyen másik objektumhoz. Az alábbi dictionary például neveket tárol, és mindegyikhez rendel egy-egy számot, ami lehet például az adott nevű ember életkora:
weasleyk = {"Bill": 21, "Charlie": 19, "Percy": 15, "Fred": 13, "George": 13, "Ron": 11, "Ginny": 10}
A dictionary-k elemeit kapcsos zárójelek között ({}) soroljuk fel, a felsorolt párok két felét pedig, amelyeket kulcsnak ill. értéknek hívunk, kettősponttal választjuk el egymástól.
A dictionary-k legfontosabb funkciója, hogy bármelyik kulcshoz lekérhetjük a hozzátartozó értéket, méghozzá úgy, hogy a dictionary után szögletes zárójelbe írjuk a kulcsot:
weasleyk["Charlie"]
19
weasleyk["Ron"]
11
Ha olyan kulcsot keresünk a dictionary-ben, ami nem szerepel benne, hibát kapunk:
weasleyk["Arthur"]
KeyError: 'Arthur'
Hogy egy adott kulcs szerepel-e egy dictionary-ben, ugyanúgy tudjuk ellenőrizni, mint listák esetében:
"Arthur" in weasleyk
False
Új párokat így adhatunk hozzá a dictionary-hez:
weasleyk["Arthur"] = 41
És hasonlóképp tudjuk módosítani az egy már létező kulcshoz tartozó értéket:
weasleyk["Arthur"] = 42
Ha for ciklussal megyünk végig egy dictionary elemein, akkor a kulcsain megyünk végig. Az alábbi ciklus például minden Weasley-t egy évvel "öregít":
for nev in weasleyk:
weasleyk[nev] += 1
weasleyk
{'Arthur': 43, 'Bill': 22, 'Charlie': 20, 'Fred': 14, 'George': 14, 'Ginny': 11, 'Percy': 16, 'Ron': 12}
Ha szükségünk van rá, egy dictionary összes kulcsát, összes értékét, vagy összes kulcs-érték párját is lekérhetjük:
list(weasleyk.keys())
['Charlie', 'Percy', 'Ron', 'Bill', 'Ginny', 'George', 'Fred', 'Arthur']
weasleyk.values()
dict_values([20, 16, 12, 22, 11, 14, 14, 43])
list(weasleyk.items())
[('Charlie', 20), ('Percy', 16), ('Ron', 12), ('Bill', 22), ('Ginny', 11), ('George', 14), ('Fred', 14), ('Arthur', 43)]
Ezek a típusok nem listák, hanem annál "okosabb" típusok, de egyrészt bármikor listává tudjuk őket alakítani:
list(weasleyk.keys())
['Charlie', 'Percy', 'Ron', 'Bill', 'Ginny', 'George', 'Fred', 'Arthur']
Másrészt egy for ciklussal végig tudunk menni az elemein:
for eletkor in weasleyk.values():
print(eletkor)
20 16 12 22 11 14 14 43
Az items által létrehozott listák elemei tuple-ök:
list(weasleyk.items())[3]
('Bill', 22)
Fontos: A dictionary-k elemeinek nincsen sorrendjük! Amikor egy for cilussal bejárjuk az elemeit, egy véletlenszerű sorrendben fogjuk egymásután kapni a kulcsokat.
Az értékek bármilyen olbjektumok lehetnek, a kulcsok viszont nem. Az általunk már ismert típusok közül a listák nem lehetnek kulcsok:
d = {}
# d[[1,2]] = 3
Lehetnek viszont tuple-ök:
d[(1,2)] = 3
d
{(1, 2): 3}
Egy kulcshoz tartozó érték lehet akár egy újabb dictionary, így már összetettebb adatot is tárolhatunk egy-egy objektumban:
hallgato = {"vezetéknév": "Róbert",
"keresztnév": "Gida",
"felhasználónév": "robertgida",
"születésnap": (1920, 8, 21),
"kedvenc": {"étel": "rakott krumpli",
"állat": "medve",
"szín": "kék",
"zene": "Halász Judit"},
"hobbik": ["fáramászás", "színezés", "sütievés"]}
hallgato["kedvenc"]["étel"]
'rakott krumpli'
Készítsük el a filmes adatunk egy ilyen reprezentációját:
cim_szerint = {}
for film in filmek:
cim = film[0].strip()
ev = int(film[1])
mufajok = film[2].split(',')
cim_szerint[cim] = {"ev": ev, "mufajok": mufajok}
Most olyan dictionary-ben tároljuk az adatot, aminek a kulcsai a filmcímek, így nagyon könnyű cím alapján lekérni egy-egy filmet:
cim_szerint["Die Hard"]
{'ev': 1988, 'mufajok': ['action', 'thriller']}
cim_szerint["Toy Story"]
{'ev': 1995, 'mufajok': ['animation', 'children', 'comedy']}
De ugyanígy elkészíthetnénk azt a dictionary-t is, ami az évekhez rendel filmeket, ekkor persze egy-egy évhez egy egész lista tartozna:
ev_szerint = {}
for film in filmek:
cim = film[0].strip()
ev = int(film[1])
mufajok = film[2].split(',')
if ev not in ev_szerint:
ev_szerint[ev] = []
ev_szerint[ev].append({"cim": cim, "mufajok": mufajok})
ev_szerint[1986]
[{'cim': 'Platoon', 'mufajok': ['drama', 'war']}, {'cim': 'Top Gun', 'mufajok': ['action', 'romance']}, {'cim': 'Jean de Florette', 'mufajok': ['drama']}, {'cim': 'Manon of the Spring', 'mufajok': ['drama']}, {'cim': 'Aliens', 'mufajok': ['action', 'sci_fi', 'thriller', 'war']}, {'cim': 'Room with a View, A', 'mufajok': ['drama', 'romance']}, {'cim': 'Star Trek IV: The Voyage Home', 'mufajok': ['action', 'adventure', 'sci_fi']}, {'cim': 'Transformers: The Movie, The', 'mufajok': ['action', 'animation', 'children', 'sci_fi', 'thriller', 'war']}, {'cim': 'Highlander', 'mufajok': ['action', 'adventure']}, {'cim': 'Down by Law', 'mufajok': ['comedy', 'drama']}, {'cim': 'Stand by Me', 'mufajok': ['adventure', 'comedy', 'drama']}, {'cim': "April Fool's Day", 'mufajok': ['comedy', 'horror']}, {'cim': 'Nosferatu a Venezia', 'mufajok': ['horror']}, {'cim': 'Invitation, The', 'mufajok': ['drama']}, {'cim': 'Two Friends', 'mufajok': ['drama']}]
Hogy milyen szerkezetű adatot építünk, az attól függ, hogyan akarjuk majd használni. Ha például szeretnénk tudni műfaj alapján szűkíteni és utána adott évre is keresni, akkor eszerint kell felépítenünk az adatunkat:
mufaj_ev_szerint = {}
for film in filmek:
cim = film[0].strip()
ev = int(film[1])
mufajok = film[2].split(',')
for mufaj in mufajok:
if mufaj not in mufaj_ev_szerint:
mufaj_ev_szerint[mufaj] = {}
if ev not in mufaj_ev_szerint[mufaj]:
mufaj_ev_szerint[mufaj][ev] = []
mufaj_ev_szerint[mufaj][ev].append(cim)
Ekkor az 1986-os akciófilmeket így tudom kilistázni:
mufaj_ev_szerint["action"][1986]
['Top Gun', 'Aliens', 'Star Trek IV: The Voyage Home', 'Transformers: The Movie, The', 'Highlander']
Az 1998-as drámákat pedig így:
mufaj_ev_szerint["drama"][1998]
['Apt Pupil', 'Desperate Measures', 'Wag the Dog', 'Desperate Measures', 'Great Expectations', 'Dangerous Beauty', 'Nil By Mouth', 'Twilight', 'Wild Things', 'Primary Colors', 'Mercury Rising', 'Four Days in September', 'Newton Boys, The', 'Truman Show, The', 'Letter From Death Row, A', 'Hurricane Streets', 'Sliding Doors', 'Mighty, The', 'Man in the Iron Mask, The', 'Duoluo tianshi', 'Magic Hour, The', 'Price Above Rubies, A', 'Hurricane Streets', 'Tokyo Fist', 'Butcher Boy, The', 'Men With Guns', 'Hana-bi', 'Niagara, Niagara', 'Butcher Boy, The', 'Spanish Prisoner, The', 'Further Gesture, A', "Mat' i syn", 'Sliding Doors']
Olvassuk be a második filmes adatbázisunkat is egy dictionary-be! Először használjuk az eredeti beolvasó függvényt:
def adatot_beolvas(fajlnev):
f = open(fajlnev, 'r', encoding="utf-8")
fejlec = f.readline().strip().split('\t')
mezok_szama = len(fejlec)
adat = []
for i in range(mezok_szama):
adat.append([])
print('mezok szama:', mezok_szama)
for sor in f:
mezok = sor.strip('\n').split('\t')
for i in range(mezok_szama):
adat[i].append(mezok[i].strip())
return adat, fejlec
import os
adat_fajl = os.path.join("data", "movie_data.tsv")
adat, fejlec = adatot_beolvas(adat_fajl)
mezok szama: 28
fejlec
['color', 'director_name', 'num_critic_for_reviews', 'duration', 'director_facebook_likes', 'actor_3_facebook_likes', 'actor_2_name', 'actor_1_facebook_likes', 'gross', 'genres', 'actor_1_name', 'movie_title', 'num_voted_users', 'cast_total_facebook_likes', 'actor_3_name', 'facenumber_in_poster', 'plot_keywords', 'movie_imdb_link', 'num_user_for_reviews', 'language', 'country', 'content_rating', 'budget', 'title_year', 'actor_2_facebook_likes', 'imdb_score', 'aspect_ratio', 'movie_facebook_likes']
Majd írjunk egy függvényt, ami dict-ekbe konvertálja az adatot. Azt is meg kell tudnunk adni a függvénynek, hogy melyik mező legyen a kulcs:
def adatbol_dict(adat, fejlec, kulcs_mezo):
kimenet = {}
mezok_szama = len(adat)
adat_meret = len(adat[0])
kulcs_oszlop = fejlec.index(kulcs_mezo)
for i in range(adat_meret):
kulcs = adat[kulcs_oszlop][i]
if kulcs not in kimenet:
kimenet[kulcs] = []
film = {}
for n in range(mezok_szama):
mezo = fejlec[n]
ertek = adat[n][i]
film[mezo] = ertek
kimenet[kulcs].append(film)
return kimenet
Próbáljuk ki, éptsünk adatot, amiben a főszereplő neve a kulcs:
szinesz_szerint = adatbol_dict(adat, fejlec, "actor_1_name")
Most nézzük meg, mit tudunk Brad Pitt első filmjéről az adatban:
szinesz_szerint["Brad Pitt"][0]
{'actor_1_facebook_likes': '11000', 'actor_1_name': 'Brad Pitt', 'actor_2_facebook_likes': '1000', 'actor_2_name': 'Jason Flemyng', 'actor_3_facebook_likes': '919', 'actor_3_name': 'Julia Ormond', 'aspect_ratio': '2.35', 'budget': '150000000', 'cast_total_facebook_likes': '13333', 'color': 'Color', 'content_rating': 'PG-13', 'country': 'USA', 'director_facebook_likes': '21000', 'director_name': 'David Fincher', 'duration': '166', 'facenumber_in_poster': '2', 'genres': 'Drama|Fantasy|Romance', 'gross': '127490802', 'imdb_score': '7.8', 'language': 'English', 'movie_facebook_likes': '23000', 'movie_imdb_link': 'http://www.imdb.com/title/tt0421715/?ref_=fn_tt_tt_1', 'movie_title': 'The Curious Case of Benjamin Button', 'num_critic_for_reviews': '362', 'num_user_for_reviews': '822', 'num_voted_users': '459346', 'plot_keywords': 'deformed baby|diary|lingerie slip|older man younger woman relationship|premature aging', 'title_year': '2008'}
szinesz_szerint["Brad Pitt"][-1]
{'actor_1_facebook_likes': '11000', 'actor_1_name': 'Brad Pitt', 'actor_2_facebook_likes': '422', 'actor_2_name': 'Tina Louise', 'actor_3_facebook_likes': '329', 'actor_3_name': 'Nick Cave', 'aspect_ratio': '1.85', 'budget': '500000', 'cast_total_facebook_likes': '11839', 'color': 'Black and White', 'content_rating': 'R', 'country': 'Switzerland', 'director_facebook_likes': '32', 'director_name': 'Tom DiCillo', 'duration': '97', 'facenumber_in_poster': '0', 'genres': 'Comedy|Music|Romance', 'gross': '', 'imdb_score': '5.8', 'language': 'English', 'movie_facebook_likes': '344', 'movie_imdb_link': 'http://www.imdb.com/title/tt0104567/?ref_=fn_tt_tt_1', 'movie_title': 'Johnny Suede', 'num_critic_for_reviews': '10', 'num_user_for_reviews': '24', 'num_voted_users': '3013', 'plot_keywords': 'idol|musician|vomiting|watching television|wyoming', 'title_year': '1991'}
Látható, hogy egy sokkal olvashatóbb formátumot hoztunk létre, amiben keresni is egyszerűbb
Nézzük meg, miből épül fel egy kivétel, okozzunk egyet:
4/0
---------------------------------------------------------------------------
ValueError
Traceback (most recent call last)
<ipython-input-73-2107a36c2657> in <module>()
----> 1 int("pingvin")
ZeroDivisionError: division by zero
Az utolsó sor megadja a kivétel típusát (jelen esetbenZeroDivisionError
), ezt követi bármilyen további információ, amit tudhatunk az adott hiba okáról. Az utolsó sor előtti rész mutatja meg, hogy melyik sorok futtatásánál keletkezett a hiba. Nézzünk még egy példát:
int("pingvin")
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-73-2107a36c2657> in <module>()
----> 1 int("pingvin")
ValueError: invalid literal for int() with base 10: 'pingvin'
Amikor egy szót próbálunk számmá konvertálni, ValueError
keletkezik, de ennél többet is tudunk, a : utáni rész részletezi a hibát, ti. hogy a "pingvin"
nem értelmezhető számként (10-es számrendszerben).
Kivételkezelésnek azt hívjuk, amikor a kódot előre felkészítjük arra, hogy bizonyos típusú kivételeket "elviseljen", vagyis ha adott típusú hibákat okoz a futása, akkor ne álljon le, hanem valamit reagáljon. Az alábbi kód például beolvas egy számot, és ha nem tudja int-té konvertálni, akkor ezt írja ki.
lista = [10, 11, 12, 'asdf', 13]
for elem in lista:
try:
szam = int(elem)
print(szam, " négyzete ", szam ** 2)
except ValueError:
print(elem, " >> nem szám!")
10 négyzete 100 11 négyzete 121 12 négyzete 144 asdf >> nem szám! 13 négyzete 169
A try
és except
szavak közötti blokkba kell írni azokat a parancsokat, amelyek során hibára számítunk. Az except után kell felsorolni azokat a hibatípusokat, amelyeket "el kell kapni", és ezt követi az a blokk, ami megadja, a hiba jelentkezése esetén milyen kód fusson le.
Így aztán például megírhatunk egy fájl-beolvasó függvényt úgy, hogy ne okozzon problémát, ha a fájl egy-két sora hibás, vagy csak másmilyen, mint a többi.