Methoden en dictionaries

Deze pagina bevat uitvoerbare code.

Terug naar lijsten

In PGM1 week 7 zag je dat een lijst mutable is: L[i] = x past de lijst zelf aan, in plaats van dat er een nieuwe lijst ontstaat. Die kennis gebruik je vandaag verder. Identiteit, waarde en shallow/deep copy komen hier niet terug.

Data die zelf iets doet

Tot nu toe deed jij iets met data. De functie staat vooraan en de data gaat erin:

len(my_list)
sum(my_list)
my_list + [9]

Vanaf nu zie je het ook andersom:

my_list.append(9)

Hier staat de lijst vooraan en de handeling erachter. Je vraagt niet aan een functie om iets met de lijst te doen; je vraagt het aan de lijst zelf.

Zo’n handeling die bij de data zelf hoort heet een methode. Je schrijft hem met een punt: eerst het ding, dan de punt, dan wat het moet doen.

my_list = [11, 21]
my_list.append(42)
my_list
[11, 21, 42]

Let op wat er niet gebeurt: er komt geen nieuwe lijst uit. append geeft niets terug, hij verandert de lijst die er al was.

my_list = [11, 21]
resultaat = my_list.append(42)
print(resultaat)
None

None, dus. Dit is dezelfde mutatie als my_list[0] = 42, alleen anders opgeschreven.

Daarmee heb je twee manieren om er een element bij te zetten:

Wat er gebeurt

Schrijfwijze

Er komt een nieuwe lijst

my_list = my_list + [9]

De lijst zelf verandert

my_list.append(9)

Allebei goed. Wanneer het verschil uitmaakt zie je verderop in de cursus.

Neem dit voorlopig aan

Een methode is een handeling die bij een object hoort: data met handelingen eraan vast. Wat een object precies is en hoe je er zelf een maakt, zie je verderop in Programmeren 2, in week 5.

Voor nu is het een schrijfwijze die je moet herkennen en kunnen gebruiken. Je komt hem overal tegen, want vrijwel alles in Python werkt zo, en dat is ook de reden dat je hem nu al nodig hebt.

Dictionaries

Dictionaries zijn willekeurige containers

d = {47: 2, 42: 1}

Elementen (of waarden) worden opgehaald met een sleutel op een willekeurige positie:

d[47] == 2
d[42] == 1

Goed nieuws, sleutels kunnen ook andere typen dan int zijn!

Van Dale

Een bekende structuur

  • woord ⟶ verklaring

  • naam ⟶ telefoonnummer

  • afkorting ⟶ betekenis

  • dier ⟶ jaren Chinese dierenriem

Chinese dierenriem

Dictionaries zijn in

zodiac_years = {
    "rabbit": [1999, 1987, 1975],
    "ox": [1997, 1985, 1973],
    "dragon": [2000, 1988]
}

De sleutels zijn hier strings en de bijbehorende waarden zijn lists. Dit voorbeeld gaat over de jaren per dier in de Chinese dierenriem, zie het Wikipedia artikel voor de volledige lijst.

Is "dragon" een sleutel in zodiac_years?

"dragon" in zodiac_years
True

Is 1969 een waarde in zodiac_years["dragon"]?

1969 in zodiac_years["dragon"]
False

Woorden tellen

list_of_words = ["spam", "spam", "taart", "spam"]
word_count = {}
for word in list_of_words:
    if word not in word_count:
        word_count[word] = 1
    else:
        word_count[word] += 1
word_count
{'spam': 3, 'taart': 1}

Dictionary-methoden

Tellen zoals hierboven kan korter. .get(sleutel, standaardwaarde) zoekt op zonder een KeyError te riskeren: bestaat de sleutel niet, dan krijg je de standaardwaarde terug in plaats van een foutmelding.

word_count.get("spam", 0)
3
word_count.get("koekje", 0)
0

Daarmee kan het tellen in één regel per woord, zonder if ... else:

word_count[word] = word_count.get(word, 0) + 1

Twee andere methoden geven je de sleutels of de waarden apart:

list(word_count.keys())
['spam', 'taart']
list(word_count.values())
[3, 1]

En .items() geeft je beide tegelijk, als paren (sleutel, waarde). Zo’n paar is een tuple - hetzelfde soort tuple dat je in PGM1 week 7 al bent tegengekomen. Dat maakt het doorlopen van een dictionary korter: in plaats van word_count[word] telkens opnieuw op te zoeken, pak je de waarde meteen mee.

for word, count in word_count.items():
    print(word, "komt", count, "keer voor")
spam komt 3 keer voor
taart komt 1 keer voor

Opdrachten

Opdracht 1

Een methodeaanroep kan het ding zelf veranderen, iets teruggeven, of allebei. In de cel hieronder staan een lijst L en een dictionary d, met drie aanroepen erop.

Voorspel eerst voor elke aanroep of L of d erdoor verandert, en wat de aanroep teruggeeft:

Aanroep

Verandert L of d?

Wat komt er terug?

L.append(1)

d.get("peer", 0)

d.get("kiwi", 0)

Voer daarna de cel hieronder uit en vergelijk de uitvoer met je voorspelling.

L = [3, 1, 4]
d = {"appel": 12, "peer": 3}

print(L.append(1))
print(L)
print(d.get("peer", 0))
print(d.get("kiwi", 0))
print(d)

Opdracht 2

Een winkel houdt de prijzen bij in een dictionary. Wat niet in de prijslijst staat, verkoopt de winkel niet, en dat telt voor het totaal als 0. De prijslijst prices en de boodschappenlijst shopping_list staan in de cel hieronder.

Bereken in een variabele total wat de boodschappenlijst kost. Gebruik .get, zodat "koffie" geen KeyError oplevert.

Hint

Begin met total = 0 en loop de boodschappenlijst langs, zoals bij het tellen van woorden. prices.get(item, 0) geeft de prijs, of 0 als het artikel er niet in staat.

prices = {"brood": 3, "melk": 2, "kaas": 5}
shopping_list = ["brood", "melk", "melk", "koffie"]

# jouw oplossing
assert total == 7

Opdracht 3

Met zodiac_years["rabbit"] zoek je van sleutel naar waarde: van dier naar jaren. De andere kant op, van jaar naar dier, heeft een dictionary geen opzoekmogelijkheid. Dan loop je hem langs.

Druk het dier af dat bij year hoort. Staat het jaar nergens in zodiac_years, dan drukt je code niets af.

year

Uitvoer

1985

ox

1988

dragon

1969

niets

Zet year in de cel hieronder achtereenvolgens op elk jaar uit de tabel en voer de cel telkens opnieuw uit.

Hint

for animal, years in zodiac_years.items(): geeft je bij elke stap een dier en zijn lijst met jaren. Met in vraag je of year in die lijst staat.

year = 1985

# jouw oplossing

Tot slot

Je kunt nu in een dictionary opzoeken, tellen en hem langslopen. In het tweede college van deze week pas je dat toe op een hele tekst. Je maakt kennis met de set, en je bouwt een dictionary die bijhoudt welk woord op welk woord volgt.