Werken met dicts

Inleiding

Woordenboeken zijn een veelgebruikte type collectie binnen Python. In eerste-stappen.adoc#_dictionaries zag je ze al even voorbij komen, in dit hoofdstuk leer je er uitgebreider mee werken.

Leerdoelen

Aan het einde van dit hoofdstuk:

  • Begrijp je wat een woordenboek is

  • Begrijp je in welke situatie je woordenboeken toe kunt passen

  • Begrijp je hoe je met elementen in een woordenboek kunt werken

Woordenboeken: een inleiding

In een (echt) woordenboek zoek je een bepaald woord op om daar de definitie of vertaling van te leren. In Python werken woordenboeken net zo. Een woordenboek, dictionary in het Engels en in Python afgekort tot dict, is een type collectie waarbij je elementen niet opzoekt op basis van index (zoals bij lijsten), maar op basis van een sleutel. Bijvoorbeeld:

nederlands_engels = {
    "programmeertaal": "programming language",
    "opleiding": "course",
    "leraar": "teacher",
}

In dit woordenboek zie je drie elementen, elk bestaande uit een sleutel (het Nederlandse woord) en de waarde (de Engelse vertaling). Dergelijke elementen worden vaak key-value pairs genoemd. De sleutels en waarden bestaan in dit voorbeeld uit tekst, maar een dict is hier zeker niet toe beperkt.

In deze paragraaf leer je wat de kenmerken van woordenboeken zijn en in welke situaties je ze kunt gebruiken.

Kenmerken van woordenboeken

Een dict kun je op verschillende manieren maken:

lege_dict_1 = {}
lege_dict_2 = dict()
dict_met_items = {
    1: "a",
    2: "b",
}

Een key-value pair bestaat altijd uit een sleutel en een waarde, gescheiden door een dubbele punt (:). Meerdere key-value pairs in een dict scheidt je met een komma. Net als bij een list kun je achter het laatste element een komma plaatsen, maar dit is niet verplicht.

Je kunt ook een dict maken van een list (of tuple) van key-value pairs. In onderstaand voorbeeld bevat mijn_lijst drie tuples, welke allemaal een key-value pair in de dict zullen worden.

mijn_lijst = [
    ("a", 1),
    ("b", 2),
    ("c", 3),
]

mijn_dict = dict(mijn_lijst)
print(mijn_dict)
# {'a': 1, 'b': 2, 'c': 3}

De belangrijkste kenmerken van een dict zijn:

  • Een dict is een type collectie.

  • Je haalt waarden op basis van sleutels op uit een dict.

  • Een dict is muteerbaar (aanpasbaar). Je kunt elementen toevoegen, aanpassen en verwijderen.

  • De elementen hoeven niet van hetzelfde type te zijn. Dit geldt voor zowel de sleutels als de waarden.

Een aantal handelingen die je bij lijsten tegenkwam zijn ook bij een dict mogelijk, maar niet alle. Je kunt:

  • Controleren of het element i in de dict aanwezig is met i in mijn_dict.

  • Het aantal elementen ophalen met len(mijn_dict).

  • Het kleinste en grootste element ophalen met min(mijn_dict) en max(mijn_dict).

Verder zijn er nog specifieke kenmerken voor de sleutels en de waarden.

Sleutels

De sleutels in een dict moeten uniek te zijn. Het volgende heeft dust geen zin:

dubbel = {
    "a": 1,
    "a": 2,
}

Zoals je verderop zult zien, haal je waarden uit een dict op basis van de sleutel op. Met dubbele sleutels weet Python niet welke waarde het terug moet geven. Je kunt de dict wel op deze manier aanmaken, maar enkel de laatste a zal opgeslagen worden.

Daarnaast moeten sleutels een onveranderlijk (immutable) type zijn. Vaak zul je tekst of integers als sleutel gebruiken, maar floats en tuples zijn ook toegestaan.

Voor nu is het voldoende om over sleutels te denken als uniek en onveranderlijk. Maar feitelijk ligt het genuanceerder, het gaat er namelijk om dat sleutels hashable moeten zijn. In de praktijk betekent dit dat een veranderlijke type wel als sleutel gebruikt kan worden, als het maar elke keer dezelfde hash oplevert.

Een hash functie is een functie dat data van arbitraire grootte omzet naar data met een vaste grootte.

De hash van het woord "Python" is: 18885f27b5af9012df19e496460f9294d5ab76128824c6f993787004f6d9a7db

De hash van de zin "Python is super" is: 33b9132f634040992a01e4c77a462813cc624868c428c7cbfdb89c7a3d4ff397

Waarden

Waarden hoeven niet uniek te zijn binnen een dict en mogen ook veranderlijk zijn. Het is dus mogelijk om bijvoorbeeld een list of een andere dict als waarde te hebben:

werknemers = {
    "Ali": {
        "leeftijd": 30,
        "rol": "Manager",
        "salaris": 5000
    },
    "Marie": {
        "leeftijd": 25,
        "rol": "Ontwikkelaar",
        "salaris": 4000
    },
    "John": {
        "leeftijd": 35,
        "rol": "Analist",
        "salaris": 4500
    }
}

Hier zie je een dict met als sleutels de naam van de werknemers, en als waarden een nieuwe dict. Bijvoorbeeld: Ali is een sleutel, met de dict {"leeftijd: 30, …​} als waarde. Dit gegeven, dat de waardes van een dict zelf ook weer bestaan uit een dict, heet nesting. Dit is overigens niet beperkt tot één laag:

nested_dict = {
    "fruit": {
        "appel": {
            "kleur": "rood",
            "smaak": "zoet"
        },
        "banaan": {
            "kleur": "geel",
            "smaak": "zoet"
        }
    },
    "groente": {
        "wortel": {
            "kleur": "oranje",
            "smaak": "hartig"
        },
        "sla": {
            "kleur": "groen",
            "smaak": "knapperig"
        }
    }
}

Zolang je laptop of computer genoeg geheugen heeft, is er geen beperking aan het aantal lagen dat je kunt nesten.

De waarden van een dict kunnen van elk type zijn.

Let op de leesbaarheid van dicts in het algemeen en bij geneste dicts in het bijzonder. Gebruik inspringingen om duidelijk te maken tot welk niveau elke laag hoort.

Wanneer woordenboeken te gebruiken

Een dict is erg geschikt om te werken met data dat verschillende eigenschappen heeft. In werken-met-lijsten.adoc#_wanneer_gebruik_je_een_lijst zag je het voorbeeld van een list voorbij komen met een aantal namen. Dit zou je nu kunnen uitbreiden naar een list met dicts. Bijvoorbeeld:

# Als lijst, alleen de naam
gebruikers = ["John", "Ali", "Marie", ]

# Als dict, met meer informatie
gebruikers = [
    {"naam": "John", "leeftijd": 30},
    {"naam": "Ali", "leeftijd": 30},
    {"naam": "Marie", "leeftijd": 25},
]

Werken met elementen in een woordenboek

Zoals inmiddels duidelijk is, bestaat een dict uit key-value pairs. Dit feit bepaalt de werking van hoe je met elementen in een dict werkt. Je zult namelijk vaak de keuze moeten maken of je wilt werken met de sleutels, de waarden of beiden.

Een dict heeft dan ook drie belangrijke methodes die je veel zult tegenkomen:

  • .keys()

  • .values()

  • .items()

Een voorbeeld maakt duidelijk wat elke methode doet:

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

print(mijn_dict.keys())
# dict_keys(['naam', 'beroep', 'leeftijd'])

print(mijn_dict.values())
# dict_values(['Erwin', 'Auteur', 38])

print(mijn_dict.items())
# dict_items([('naam', 'Erwin'), ('beroep', 'Auteur'), ('leeftijd', 38)])

Elke methode levert een iterable op, je kunt het zien als een speciaal soort lijst. Je kunt er dus ook langs itereren:

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

for key in mijn_dict.keys():
    print(key)

# naam
# beroep
# leeftijd

De .items() methode levert een lijst op waarbij elk element een tuple is van de sleutel en bijbehorende waarde, zodat je met beiden kunt werken. Een voorbeeld hiervan zie je in Itereren langs een woordenboek als je leert een for-loop te gebruiken met een dict.

De .keys() methode zul je in de praktijk zelden zien, omdat dit als de standaard wordt beschouwd. Laat je .keys() weg, dan zul je de acties uitvoeren op de sleutels van de dict. Bovenstaand voorbeeld kun je dus als volgt herschrijven:

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

for key in mijn_dict:  # Let op, geen .keys()!
    print(key)

# naam
# beroep
# leeftijd

Kenmerken van een dict ophalen

Met min(), max() en len() haal je enkele kenmerken op van een dict. Met len() haal je het aantal elementen op. Voor len() maakt het niet uit of je dit uitvoert op mijn_dict.keys(), mijn_dict.values() of mijn_dict.items(), het resultaat is logischerwijs altijd hetzelfde.

Met min() en max() haal je de kleinste en grootste sleutel of waarde op:

mijn_dict = {
    "a": 100,
    "b": 10,
    "c": 1,
}

print(len(mijn_dict))  # 3

print(min(mijn_dict))  # a
print(max(mijn_dict))  # c

print(min(mijn_dict.values()))  # 1
print(max(mijn_dict.values()))  # 100
Opdracht 1: min / max

Neem onderstaande dict. Wat is het resultaat van regel 7? Wat verwacht je dat er gebeurt als je de laatste twee regels uitvoert?

mijn_dict = {
    "a": 100,
    "b": 10,
    "c": 1,
}

print(max(mijn_dict.items()))

mijn_dict["a"] = "honderd"
print(max(mijn_dict.values()))
Klik om het antwoord te tonen

Het resultaat van regel 7 is een tuple: ('c', 1). max() kijkt standaard naar het eerste element in de tuple, en van a', 'b' en `c, is c het grootst.

Voer je de laatste twee regels uit dan zul je een foutmelding krijgen. Python weet namelijk niet hoe het integers met tekst moet vergelijken.

Elementen uit een dict ophalen

Je haalt de waarden uit een dict op op basis van de sleutel. Dit kun je op twee manieren doen. De eerste lijkt op hoe je een element uit een list ophaalt:

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

naam = mijn_dict["naam"]
print(naam)  # Erwin

Net als bij lijsten gebruik je haakjes ([]), maar in plaats van een index gebruik je de sleutel, naam in dit geval. Als de sleutel niet bestaat, krijg je een fout. Probeer maar eens om naam te veranderen in Naam, en kijk wat er gebeurt.

Om dit te voorkomen is er nog een andere manier om waarden op te halen, met de .get() methode:

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

naam = mijn_dict.get("naam")
print(naam)  # Erwin

hobby = mijn_dict.get("hobby")
print(hobby)  # None

default = mijn_dict.get("hobby", "Onbekend")
print(default)  # Onbekend

In plaats van de haakjes te gebruiken, gebruik je de .get() methode. Bestaat de sleutel niet, dan zal .get() standaard None teruggeven. Dit kun je echter aanpassen door een standaardwaarde op te geven, zoals op regel 9.

De .get() methode is handig om in if-statements te gebruiken:

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

hobby = mijn_dict.get("hobby")
if not hobby:
    print("Bezoek onze website voor inspiratie!")

Eerder zag je dat een dict meerdere lagen diep kan zijn. Het ophalen van waarden uit de diepere lagen werkt hetzelfde als het ophalen van de eerste laag:

werknemers = {
    "Ali": {
        "leeftijd": 30,
        "rol": "Manager",
        "salaris": 5000
    },
    "Marie": {
        "leeftijd": 25,
        "rol": "Ontwikkelaar",
        "salaris": 4000
    },
    "John": {
        "leeftijd": 35,
        "rol": "Analist",
        "salaris": 4500
    }
}

rol_ali = werknemers["Ali"]["rol"]
print(rol_ali)  # Manager

Je verwacht misschien dat je dit ook kunt doen met meerdere aanroepen van .get(). En dit werkt ook, maar levert alsnog fouten op als de eerste sleutel niet bestaat:

werknemers = {
    "Ali": {
        "leeftijd": 30,
        "rol": "Manager",
        "salaris": 5000
    }
}

rol_erwin = werknemers.get("Erwin").get("rol")
# Sleutel 'Erwin' bestaat niet
# Fout: AttributeError

Omdat de sleutel Erwin niet bestaat, wordt er None teruggegeven. En None heeft geen methode .get(), waardoor een fout ontstaat. Er zijn manieren om dit op te lossen, één hiervan zul je leren in Werken met uitzonderingen, waar je leert hoe je fouten af kunt handelen.

Opdracht 2: get it?

Schrijf code dat onderstaande dict neemt, en de naam van de werknemer en zijn salaris print. Als de werknemer niet bestaat, print dan "Werknemer niet gevonden." Als het salaris niet wordt gevonden, print dan "Het salaris van <werknemer> is niet bekend".

werknemers = {
    "Ali": {
        "leeftijd": 30,
        "rol": "Manager",
        "salaris": 5000
    },
    "Marie": {
        "leeftijd": 25,
        "rol": "Ontwikkelaar",
        "salaris": 4000
    },
    "John": {
        "leeftijd": 35,
        "rol": "Analist",
    }
}
Klik om het antwoord te tonen

Een mogelijke uitwerking is:

te_zoeken = "Ali"
werknemer = werknemers.get(te_zoeken)
if werknemer:
    salaris = werknemer.get("salaris")
    if salaris:
        print(f"Het salaris van {te_zoeken} is {salaris}.")
    else:
        print(f"Het salaris van {te_zoeken} is niet bekend.")
else:
    print("Werknemer niet gevonden.")

# Een alternatieve manier, iets korter:
te_zoeken = "Ali"
werknemer = werknemers.get(te_zoeken)
if werknemer and werknemer.get("salaris"):
    print(f"Het salaris van {te_zoeken} is {werknemer.get('salaris')}.")
elif werknemer:
    print(f"Het salaris van {te_zoeken} is niet bekend.")
else:
    print("Werknemer niet gevonden.")

Controleren of een element zich in een woordenboek bevindt

Net als bij een list, kun je in (en not in) gebruiken om te controleren of een element zich in een dict bevindt. Hierbij gaat het echter niet om de key-value pair, maar controleer je op de sleutel óf de waarde.

Gebruik je in, dan controleer je standaard op de sleutels:

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

print("naam" in mijn_dict)  # True, "naam" is een sleutel
print("Erwin" in mijn_dict)  # False, "Erwin" is géén sleutel

Om te controleren of een waarde zich in een dict bevindt, voeg je .values() toe aan de dict:

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

print("naam" in mijn_dict.values())  # False, "naam" is geen waarde
print("Erwin" in mijn_dict.values())  # True, "Erwin" is een waarde

Itereren langs een woordenboek

Omdat een dict een collectie is, kun je for gebruiken om langs de dict te itereren. Bekijk wat er gebeurt als je dit doet:

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

for element in mijn_dict:
    print(element)

# naam
# beroep
# leeftijd

Je ziet dat elke sleutel wordt afgedrukt. Net als bij in, kun je .values() gebruiken om de waarden op te halen:

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

for element in mijn_dict.values():
    print(element)

# Erwin
# Auteur
# 38

Als je for gebruikt zonder .values(), itereer je over de sleutels. Je kunt dan ook dit schrijven:

for element in mijn_dict.keys():
    print(element)

Maar je mag .keys() dus ook weglaten. Hetzelfde geldt bij in en andere acties. Het uitgangspunt is dat je acties uitvoert op de sleutels, daarom mag je .keys() weglaten.

In de meeste gevallen zul je echter willen itereren langs de sleutels én de waarden. Gebruik hiervoor .items():

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

for key, value in mijn_dict.items():
    print(f"{key}: {value}")

# naam: Erwin
# beroep: Auteur
# leeftijd: 38

Je ziet dat je ook twee variabelen in de for-loop gebruikt. In het voorbeeld heten ze key en value, en dit zul je vaak zien. Maar je mag hier kiezen wat je wilt, bijvoorbeeld kenmerk en waarde.

Opdracht 3: Werknemers

Neem weer onderstaande dict met werknemersgegevens. Schrijf code dat voor elke werknemer alle gegevens per werknemer print.

# Gegevens
werknemers = {
    "Ali": {
        "leeftijd": 30,
        "rol": "Manager",
        "salaris": 5000
    },
    "Marie": {
        "leeftijd": 25,
        "rol": "Ontwikkelaar",
        "salaris": 4000
    },
    "John": {
        "leeftijd": 35,
        "rol": "Analist",
        "salaris": 4500
    }
}

# Gewenst resultaat
# Ali
# ---
# Leeftijd: 30
# Rol: Manager
# Salaris: 5000
#
# Marie
# -----
# Leeftijd: 25
# Rol: Ontwikkelaar
# Salaris: 4000
#
# John
# ----
# Leeftijd: 35
# Rol: Analist
# Salaris: 4500
Klik om het antwoord te tonen

Een mogelijke uitwerking is:

for werknemer, gegevens in werknemers.items():
    print(f"{werknemer}")
    print("-"*len(werknemer))
    print(f"Leeftijd: {gegevens.get('leeftijd')}")
    print(f"Rol: {gegevens.get('rol')}")
    print(f"Salaris: {gegevens.get('salaris')}")
    print()

Elementen wijzigen of toevoegen

Tot dusver heb je steeds een dict gemaakt met een aantal key-value pairs. Een dict is echter aanpasbaar, en het is dus ook mogelijk om later elementen toe te voegen.

De meest eenvoudige manier is als volgt:

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

mijn_dict["hobby"] = "Schrijven"
print(mijn_dict["hobby"])  # Schrijven

Je voegt tussen haakjes de sleutel toe en wijst vervolgens de waarde toe aan die sleutel.

Gebruik je een bestaande sleutel, dan zal de oude waarde overschreven worden:

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
    "hobby": "Schrijven",
}

mijn_dict["hobby"] = "Lezen"
print(mijn_dict["hobby"])  # Lezen

Een tweede manier om elementen aan een dict toe te voegen is met de .update() methode. Hiermee voeg je echter niet één key-value pair toe, maar voeg je twee dicts samen, of voeg je een andere iterable met key-value pairs (zoals een list) toe aan de dict.

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

tweede_dict = {
    "hobby": "Schrijven",
    "taal": "Python",
}

mijn_dict.update(tweede_dict)
print(mijn_dict)
# {
#   'naam': 'Erwin',
#   'beroep': 'Auteur',
#   'leeftijd': 38,
#   'hobby': 'Schrijven',
#   'taal': 'Python'
# }

mijn_list = [("lengte", 1.78), ("favoriet_dier", "Cobra"), ]
mijn_dict.update(mijn_list)
print(mijn_dict)
# {
#   'beroep': 'Auteur',
#   'favoriet_dier': 'Cobra',
#   'hobby': 'Schrijven',
#   'leeftijd': 38,
#   'lengte': 1.78,
#   'naam': 'Erwin',
#   'taal': 'Python'
# }

Als je dict steeds uitgebreider wordt, zal het steeds minder leesbaar worden als je simpelweg print() gebruikt. Alles zal namelijk op één regel worden geprint.

Je kunt hiervoor pprint gebruiken. Voeg bovenaan je bestand het volgende toe:

from pprint import pprint

Druk nu de dict af met: pprint(mijn_dict) en zie het verschil.

In een later hoofdstuk leer je meer over imports.

Opdracht 4: Geneste toevoeging

Neem onderstaande dict en voeg de volgende zaken toe:

  • Een sleutel kinderen, met als waarde een list met twee kinderen.

  • Elk kind heeft weer de volgende gegevens:

    • Leeftijd

    • Naam

  • Een sleutel moeder, met dezelfde gegevens als vader.

gezin = {
    "vader": {
        "naam": "John",
        "leeftijd": 38,
    }
}
Klik om het antwoord te tonen

Een mogelijke uitwerking is:

from pprint import pprint

kinderen = [
    {"naam": "Lotte", "leeftijd": 3},
    {"naam": "Jaap", "leeftijd": 6},
]

moeder = {"naam": "Liesbeth", "leeftijd": 39}

gezin["kinderen"]= kinderen
gezin["moeder"] = moeder

pprint(gezin)

In de alternatieve oplossing werkt if werknemer and werknemer.get("salaris"): omdat als werknemer niet bestaat, de rest van de if-statement niet meer geëvalueerd wordt.

Elementen verwijderen

Net als bij een list, kun je bij een dict ook del gebruiken om elementen te verwijderen.

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

del mijn_dict["leeftijd"]
print(mijn_dict)
# {'naam': 'Erwin', 'beroep': 'Auteur'}

Ook .pop() kun je bij een dict gebruiken, en net als bij een list verwijdert je hiermee het element én geef je het terug:

mijn_dict = {
    "naam": "Erwin",
    "beroep": "Auteur",
    "leeftijd": 38,
}

naam = mijn_dict.pop("naam")
print(naam)  # Erwin
print(mijn_dict)
# {'beroep': 'Auteur', 'leeftijd': 38}

In tegenstelling tot .pop() bij een list, geef je de sleutel op, niet een index. Gebruik je .pop() zonder index bij een list, dan zal het het laatste element teruggeven. Bij een dict moet je .pop() altijd een waarde meegeven.

Elementen sorteren en omkeren

Een dict kun je sorteren en omkeren, net als je in het vorige hoofdstuk leerde bij lijsten. In tegenstelling tot bij een list, heeft dict geen eigen methodes .sort() en .reverse(). Je zult dus de ingebouwde functies sorted() en reversed() moeten gebruiken.

In Python hebben dicts pas vanaf versie 3.7 een vaste volgorde. Dit betekent dat de volgorde van de elementen bij het aanmaken van de dict ook de volgorde is als je er met een for-loop overheen itereert of als je het print.

Dit had ook gevolgen voor het sorteren: je kon wel sorteren en het opslaan in een (nieuwe) dict, maar omdat de volgorde niet behouden bleef, kon je de sortering ook weer verliezen.

Loop je tegen problemen met sorteren en omkeren aan, controleer dan of je Python versie 3.7 of hoger is. Typ python --version in je shell om de versie te verkrijgen.

Sorteren

Je kunt een dict sorteren op basis van zowel de sleutels als de waarden. Sorteren op waarden is iets complexer en laten we hier buiten beschouwing. Om te sorteren op basis van de sleutel gebruik je de ingebouwde functie sorted(). Echter, sorted() geeft altijd een list terug:

personen = {
    "Marie": 36,
    "Ali": 35,
    "John": 38,
}

alfabetisch = sorted(personen)
# ["Ali", "John", "Marie"]

Dit kan nuttig zijn, maar hiermee is de dict zelf niet gesorteerd. Om dit te bereiken gebruik je de .items() methode, die je ook hebt gebruikt in de for-loop. Met .items() maak je een iterable van key-value pairs:

personen = {
    "Marie": 36,
    "Ali": 35,
    "John": 38,
}

print(personen.items())
# dict_items([('Marie', 36), ('Ali', 35), ('John', 38)])

Omdat sorted() met iterables werkt en .items() een iterable oplevert, kun je beiden combineren om te sorteren:

personen = {
    "Marie": 36,
    "Ali": 35,
    "John": 38,
}

alfabetisch = sorted(personen.items())
print(alfabetisch)
# [('Ali', 35), ('John', 38), ('Marie', 36)]

alfabetisch = dict(alfabetisch)
print(alfabetisch)
# {'Ali': 35, 'John': 38, 'Marie': 36}

Omdat sorted() een list oplevert, maak je er met dict() weer een dict van. Dit kan overigens ook in één regel:

alfabetisch = dict(sorted(personen.items()))

Omkeren

Een dict keer je om met reversed() en werkt vergelijkbaar als sorted():

personen = {
    "Marie": 36,
    "Ali": 35,
    "John": 38,
}

omgekeerd = dict(reversed(personen.items()))
print(omgekeerd)
# {'John': 38, 'Ali': 35, 'Marie': 36}