Dictionaries — paren van key en value
Waarden opslaan en ophalen op naam, KeyError vermijden met .get(), keys toevoegen en verwijderen, doorlopen met .items(), en waarom een lijst geen key kan zijn.
Hoe wil je leren?
Het probleem dat we oplossen
De cijfers van drie mensen moeten vastgelegd worden — met wie welk cijfer heeft.
Lijsten bieden maar één manier: twee lijsten, naast elkaar.
names = ["rafi", "ahmed", "bilal"]
marks = [72, 45, 90]
print(names[1], marks[1])ahmed 45Het werkt. En de hele opzet rust op jouw discipline, zonder enige hulp van Python.
Voeg je iemand toe aan names en vergeet je marks? De twee lijsten hebben nu verschillende lengtes, en vanaf dat moment beschrijven names[2] en marks[2] twee verschillende mensen. Python zegt geen woord. names sorteren? De relatie breekt volledig, en stilzwijgend.
Hier zit de kern: de relatie tussen die twee dingen staat nergens opgeschreven. Die relatie is alleen "dezelfde index" — toeval dat op elk moment kan breken.
Een dictionary schrijft die relatie in de code. Namen in plaats van indexen, en het paar bij elkaar gehouden op één plek.
Aan het einde van dit hoofdstuk kun je
- Een dictionary bouwen en een waarde ophalen op de key
- Een
KeyErrorvermijden met.get(), en weten wanneer welke van de twee goed is - Keys toevoegen, wijzigen en verwijderen
- Een dictionary doorlopen met
.keys(),.values()en.items() - Uitleggen waarom een lijst geen key kan zijn maar een tuple wel
Voorkennis: Tuples en unpacking.
Een dictionary — paren van key en value
Accolades, met key: value-paren erin:
marks = {"rafi": 72, "ahmed": 45, "bilal": 90}
print(marks)
print(marks["ahmed"])
print(len(marks)){'rafi': 72, 'ahmed': 45, 'bilal': 90}
45
3marks["ahmed"] — een naam, geen index. En die naam staat gewoon in de code, dus zes maanden later lees je nog steeds wat er gevraagd wordt.
len() telt de paren: drie.
Een woord over volgorde. Sinds Python 3.7 onthoudt een dictionary de volgorde waarin dingen erin gezet werden, en print hij ze ook zo. Maar een dictionary is bedoeld om dingen op te zoeken op naam, niet op positie — schrijfmarks[0], dan zoekt Python een key die het getal nul is, vindt er geen, en stopt metKeyError: 0.
Een key die er niet is — KeyError en .get()
marks = {"rafi": 72}
print(marks["dia"])KeyError: 'dia'Een KeyError betekent "er is geen key met die naam". Dezelfde fout op een lijst gaf een IndexError — een andere naam voor hetzelfde soort fout.
Wil je in plaats van een stop een veilig antwoord, gebruik dan .get():
marks = {"rafi": 72}
print(marks.get("dia"))
print(marks.get("dia", 0))
print(marks.get("rafi", 0))None
0
72Het tweede argument zegt "geef me dit als het ontbreekt". Zonder zo'n argument krijg je None.
Welke, wanneer? Als de key er zou moeten zijn, schrijf dan marks["rafi"] — het ontbreken daarvan is een bug, en de KeyError geeft hem meteen aan je door. Als het ontbreken van de key normaal is, gebruik .get(). Overal .get() gebruiken laat slechte data stilzwijgend doorslippen, en juist die slechte data probeerde je te vinden.
Toevoegen, wijzigen, verwijderen
marks = {"rafi": 72}
marks["dia"] = 88
marks["rafi"] = 75
print(marks)
del marks["rafi"]
print(marks){'rafi': 75, 'dia': 88}
{'dia': 88}Dezelfde regel doet twee dingen: ontbreekt de key, dan wordt hij toegevoegd; bestaat hij al, dan wordt hij vervangen. Er is geen aparte methode om toe te voegen.
En daar volgt een belangrijke regel uit — een key kan maar één keer voorkomen:
marks = {"rafi": 72, "rafi": 90}
print(marks){'rafi': 90}Geen fout. De tweede wiste de eerste stilzwijgend uit. Dit gebeurt regelmatig als je data uit een bestand laadt, en is een veelvoorkomende manier waarop records verloren gaan.
in zoekt de keys, niet de values
marks = {"rafi": 72, "ahmed": 45}
print("rafi" in marks)
print(72 in marks)True
False72 zit er zeker als waarde, maar in kijkt alleen bij de keys. Wil je de values doorzoeken, dan moet je dat zeggen: 72 in marks.values().
Doorlopen, op drie manieren
marks = {"rafi": 72, "ahmed": 45}
for name in marks:
print(name)
print("---")
for name, mark in marks.items():
print(name, mark)
print("---")
print(list(marks.keys()))
print(list(marks.values()))rafi
ahmed
---
rafi 72
ahmed 45
---
['rafi', 'ahmed']
[72, 45]De eerste is het vermelden waard: for name in marks geeft je de keys, niet de values. Het verbaast iedereen de eerste keer.
Wil je allebei, dan geeft .items() elke ronde een tuple terug — en name, mark unpackt hem. Hier betaalt het unpacking van het vorige hoofdstuk zich terug.
Wil je met de values rekenen, dan .values():
marks = {"rafi": 72, "ahmed": 45, "bilal": 90}
print(sum(marks.values()))
print(max(marks.values()))
print(round(sum(marks.values()) / len(marks), 2))207
90
69.0Wat een key kan zijn
Om een key te zijn, moet iets onveranderlijk zijn. Tekst, getallen en tuples werken allemaal. Een lijst niet:
d = {}
d[["a", "b"]] = 1TypeError: unhashable type: 'list'Dat woord "unhashable" is het uitleggen waard. Een dictionary vindt dingen zo snel doordat hij uit elke key een getal berekent (een hash) en dat getal gebruikt om rechtstreeks naar de goede plek te gaan. Als de key daarna zou kunnen veranderen, klopte dat getal niet meer en was het item verloren. Daarom staat Python überhaupt niets dat kan veranderen toe als key.
Een tuple kan niet veranderen, wat hem een uitstekende key maakt:
grid = {}
grid[(0, 0)] = "start"
grid[(1, 2)] = "tree"
print(grid)
print(grid[(1, 2)]){(0, 0): 'start', (1, 2): 'tree'}
treeHet vorige hoofdstuk noemde de onveranderlijkheid van een tuple een voordeel. Dit is het eerste praktische bewijs daarvan.
Het handigste patroon dat er is — tellen
words = ["pen", "bag", "pen", "ink", "pen"]
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts){'pen': 3, 'bag': 1, 'ink': 1}Lees de regel van binnenuit: counts.get(word, 0) zegt "geef me het aantal keer dat dit woord tot nu toe voorkomt, en nul als het er nog geen heeft", dan wordt er één opgeteld en het resultaat teruggezet.
De default van .get() doet hier al het werk — er zou zonder hem bij elk nieuw woord een KeyError omhoog komen, en je zou if word in counts: en twee takken nodig hebben. Je gaat dit eenregelige patroon veel schrijven.
Een compleet voorbeeld
marks.py:
# A dictionary keeps a name attached to its value
marks = {"rafi": 72, "ahmed": 45, "bilal": 90, "dia": 88}
print("Everyone:")
for name, mark in marks.items():
status = "pass" if mark >= 40 else "fail"
print(f" {name:<8} {mark:>3} {status}")
print()
print("Count :", len(marks))
print("Total :", sum(marks.values()))
print("Average :", round(sum(marks.values()) / len(marks), 2))
print("Highest :", max(marks.values()))
best = max(marks, key=marks.get)
print("Best :", best, marks[best])
print("Has dia :", "dia" in marks)
print("Missing :", marks.get("nadia", "not recorded"))
marks["dia"] = 91
marks["nadia"] = 60
del marks["ahmed"]
print()
print("After edits:", marks)Everyone:
rafi 72 pass
ahmed 45 pass
bilal 90 pass
dia 88 pass
Count : 4
Total : 295
Average : 73.75
Highest : 90
Best : bilal 90
Has dia : True
Missing : not recorded
After edits: {'rafi': 72, 'bilal': 90, 'dia': 91, 'nadia': 60}Drie dingen die het vermelden waard zijn.
status = "pass" if mark >= 40 else "fail" is een manier om een voorwaarde op één regel te schrijven, gelezen vanuit het midden naar buiten: ""pass" als mark >= 40, anders "fail"". Het vierregelige if/else van hoofdstuk negen kiest hier één waarde, dus één regel is genoeg. Schrijf voor een echte beslissing een volledige if — deze vorm is er alleen om tussen twee waarden te kiezen.
max(marks, key=marks.get) is het key= van hoofdstuk dertien met een nieuwe taak. (Hier is key= de naam van een argument van max, geen key van een dictionary.) max(marks) zou de grootste key geven, dus de laatste naam alfabetisch. key=marks.get zegt om elke key juist op de value te vergelijken, wat de naam met het hoogste cijfer oplevert. Merk op dat het hoogste cijfer vinden makkelijk is (max(marks.values())); om erachter te komen van wie het is, heb je deze truc nodig.
De volgorde op de laatste regel. dia bestond al, dus alleen de waarde veranderde, niet de positie. nadia is nieuw en werd daarom achteraan toegevoegd. En ahmed is weg. Een dictionary onthoudt de volgorde waarin keys toegevoegd werden, en het vervangen van een waarde verstoort die niet.
Wanneer het misgaat
KeyError: 'dia' Er is geen key met die naam. Controleer spelling en hoofdletters — "Rafi" en "rafi" zijn twee verschillende keys. Keys die je uit een bestand leest, hebben vaak een spatie erachter; print(list(d.keys())) zet aanhalingstekens rond elke key, zodat je het ziet. Als het ontbreken van de key normaal is, gebruik .get().
TypeError: unhashable type: 'list' Een poging om een lijst als key te gebruiken. Gebruik een tuple — tuple(my_list) maakt er een.
for x in my_dict geeft me de values niet En terecht — dat geeft de keys. Voor de values gebruik je .values(), en voor allebei .items().
Twee keys waren hetzelfde en een record verdween Een key bestaat maar één keer, en de latere verving de eerdere stilzwijgend. Wil je meerdere records onder één naam bewaren, maak de waarde dan een lijst — het onderwerp van hoofdstuk zeventien.
KeyError: 0 De code schreef my_dict[0] en vroeg om het eerste item alsof het een lijst was. Een dictionary heeft geen posities; 0 wordt opgezocht als key, en als die key er niet is, krijg je KeyError: 0. Heb je echt het eerste paar nodig, schrijf dan list(my_dict.items())[0].
.sort() werkt niet op een dictionary Klopt — dictionaries hebben geen .sort(). sorted(d.items()) geeft een gesorteerde lijst, wat meestal is wat je wilde.
Stap 4 van 6 — Voorspellen
Test je begrip
Een lus loopt direct over een dictionary. Wat wordt er afgedrukt?
marks = {"rafi": 72, "ahmed": 45}
for name in marks:
print(name)- Arafi 72 ahmed 45
- Brafi ahmed
- C72 45
- D('rafi', 72) ('ahmed', 45)
Er wordt om een key gevraagd die er niet is. Wat gebeurt er, en wat is het veilige alternatief?
marks = {"rafi": 72}
print(marks["dia"])- AHij print `None`
- BHij print een lege regel
- CEen `KeyError` — het veilige alternatief is `marks.get("dia")`, wat `None` geeft
- DEen `IndexError`
Het telpatroon. Wat wordt er afgedrukt?
words = ["pen", "bag", "pen"]
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts)- AEen `KeyError`
- B{'pen': 2, 'bag': 1}
- C{'pen': 1, 'bag': 1}
- D{'pen': 3, 'bag': 1}
Voor antwoorden heb je een account nodig
Log in om je antwoorden te controleren
De vragen staan hierboven, en ze in je hoofd uitwerken is waar het om draait. Log in om de antwoorden, de uitleg en de hints in drie niveaus te zien.
Jouw beurt
Schrijf een bestand genaamd stock.py met een dictionary van minstens zes producten — de productnaam als key, de prijs als waarde.
Daarna:
- Print elk product en zijn prijs in uitgelijnde kolommen
- Print de som, het gemiddelde, de hoogste en de laagste prijs
- Vind de naam van het duurste product, niet alleen de prijs
- Voeg een product toe, verander één prijs, verwijder één product, en print daarna alles opnieuw
- Gebruik
.get()om naar een product te vragen dat niet bestaat, met een bericht als default
Schrijf daarna een telprogramma: zet meerdere woorden in een lijst, sommige herhaald, en tel hoe vaak elk voorkomt met het patroon counts.get(word, 0) + 1.
Breek het tenslotte met opzet: vraag met vierkante haken om een key die ontbreekt, en probeer een lijst als key te gebruiken. Twee verschillende fouten komen terug — leg aan jezelf uit welke het is en waarom.
Stap 6 van 6
Uitdaging — de hoofdstukquiz
Tien vragen van makkelijk naar moeilijk. De laatste zijn met opzet lastig.
Log in om de quiz te maken