Dizionari Annidati
Impara a creare, accedere, modificare, eliminare e iterare dizionari annidati in Python con esempi chiari e best practice.
Un dizionario annidato è un dizionario che contiene altri dizionari come valori. Questo crea una struttura dati gerarchica (ad albero) ideale per rappresentare dati raggruppati del mondo reale — come una raccolta di profili utente, un file di configurazione o una risposta di un'API JSON.
Questo capitolo tratta:
- Creazione di dizionari annidati
- Accesso ai valori a qualsiasi profondità
- Modifica e aggiunta di voci
- Eliminazione di chiavi e sotto-dizionari
- Iterazione sui dizionari annidati
- Utilizzo di
.get()per evitareKeyError - Lavoro con dati profondamente annidati e JSON
Creazione di Dizionari Annidati
Definisci un dizionario i cui valori sono a loro volta dizionari:
people = {
"person1": {"name": "Alice", "age": 30},
"person2": {"name": "Bob", "age": 25},
}
print(people)
# {'person1': {'name': 'Alice', 'age': 30}, 'person2': {'name': 'Bob', 'age': 25}}Puoi anche costruire un dizionario annidato in modo incrementale:
people = {}
people["person1"] = {"name": "Alice", "age": 30}
people["person2"] = {"name": "Bob", "age": 25}
print(people["person1"]) # {'name': 'Alice', 'age': 30}Non c'è alcun limite alla profondità — un valore interno può essere a sua volta un dizionario annidato.
Accesso ai Valori dei Dizionari Annidati
Concatena le ricerche tra parentesi quadre per approfondire qualsiasi livello:
Accesso Sicuro con .get()
Le ricerche concatenate tra parentesi sollevate generano un KeyError se una chiave è mancante. Usa .get() per restituire un valore predefinito invece:
people = {
"person1": {"name": "Alice", "age": 30},
}
# Safe: returns None if "person3" does not exist
print(people.get("person3", {}).get("name", "Unknown")) # Unknown
# Risky: raises KeyError
# print(people["person3"]["name"])Il pattern .get(outer_key, {}).get(inner_key, default) è il modo idiomatico per leggere dati annidati opzionali senza un blocco try/except.
Modifica dei Valori dei Dizionari Annidati
Assegna direttamente attraverso la catena di chiavi:
Aggiunta di Nuovi Sotto-Dizionari
Assegna un nuovo dizionario letterale a una nuova chiave esterna:
Utilizzo di setdefault() per Aggiungere Solo se Mancante
setdefault() inserisce una chiave con un valore predefinito solo se non esiste già — utile quando si costruiscono dizionari annidati da un flusso di dati:
scores = {}
for student, subject, grade in [
("Alice", "math", 90),
("Alice", "english", 85),
("Bob", "math", 78),
]:
scores.setdefault(student, {})[subject] = grade
print(scores)
# {'Alice': {'math': 90, 'english': 85}, 'Bob': {'math': 78}}Eliminazione di Chiavi e Sotto-Dizionari
Usa del per rimuovere una chiave (e il suo valore) a qualsiasi livello:
people = {
"person1": {"name": "Alice", "age": 30},
"person2": {"name": "Bob", "age": 25},
"person3": {"name": "Carol", "age": 40},
}
del people["person2"]["age"] # remove one key from an inner dict
del people["person3"] # remove an entire sub-dictionary
print(people)
# {'person1': {'name': 'Alice', 'age': 30}, 'person2': {'name': 'Bob'}}Usa .pop() se hai bisogno anche del valore eliminato:
removed = people["person1"].pop("age", None)
print(removed) # 30
print(people["person1"]) # {'name': 'Alice'}Iterazione sui Dizionari Annidati
Ciclo sulle chiavi esterne e sugli elementi interni
people = {
"person1": {"name": "Alice", "age": 30},
"person2": {"name": "Bob", "age": 25},
}
for person_id, details in people.items():
print(f"{person_id}:")
for key, value in details.items():
print(f" {key}: {value}")Output:
person1:
name: Alice
age: 30
person2:
name: Bob
age: 25Appiattire un dizionario annidato in un elenco di record
Un pattern comune quando si preparano dati per l'elaborazione:
people = {
"person1": {"name": "Alice", "age": 30},
"person2": {"name": "Bob", "age": 25},
}
records = [
{"id": pid, **info}
for pid, info in people.items()
]
print(records)
# [{'id': 'person1', 'name': 'Alice', 'age': 30},
# {'id': 'person2', 'name': 'Bob', 'age': 25}]Dizionari Profondamente Annidati
Python non impone alcun limite alla profondità di annidamento. Ecco un esempio a tre livelli che rappresenta la struttura dei dipartimenti di un'azienda:
company = {
"engineering": {
"frontend": {
"lead": "Alice",
"headcount": 5,
},
"backend": {
"lead": "Bob",
"headcount": 8,
},
},
"marketing": {
"content": {
"lead": "Carol",
"headcount": 3,
},
},
}
# Access three levels deep
print(company["engineering"]["backend"]["lead"]) # Bob
# Iterate two levels and collect leads
leads = [
dept_data["lead"]
for dept_data in (
team
for teams in company.values()
for team in teams.values()
)
]
print(leads) # ['Alice', 'Bob', 'Carol']Quando si leggono chiavi profonde che potrebbero essere assenti, concatena le chiamate .get():
lead = company.get("hr", {}).get("recruitment", {}).get("lead", "Not assigned")
print(lead) # Not assignedDizionari Annidati e JSON
Gli oggetti JSON si mappano direttamente ai dizionari annidati di Python. Il modulo json converte tra di loro:
import json
data = {
"users": {
"u1": {"name": "Alice", "active": True},
"u2": {"name": "Bob", "active": False},
}
}
# Serialize to JSON string
json_str = json.dumps(data, indent=2)
print(json_str)
# Deserialize back to a nested dict
restored = json.loads(json_str)
print(restored["users"]["u1"]["name"]) # AliceQuesto è il flusso di lavoro tipico quando si consumano REST API o si leggono file di configurazione.
Errori Comuni
Trappola dei riferimenti condivisi. Se copi un dizionario interno per riferimento e poi lo modifichi, sia l'originale che la copia cambiano:
original = {"a": {"x": 1}}
shallow = original.copy() # copies only the outer dict
shallow["a"]["x"] = 99
print(original["a"]["x"]) # 99 ← original is affected!Usa copy.deepcopy() quando hai bisogno di una copia completamente indipendente:
import copy
original = {"a": {"x": 1}}
deep = copy.deepcopy(original)
deep["a"]["x"] = 99
print(original["a"]["x"]) # 1 ← original is safeConsulta il capitolo Copia di Dizionari per un confronto completo tra copia superficiale e copia profonda.
KeyError su chiavi intermedie mancanti. Scrivere d["a"]["b"] = 1 genera un KeyError se "a" non esiste ancora. Usa setdefault o collections.defaultdict per creare automaticamente i livelli intermedi.
Riferimento Rapido
| Operazione | Sintassi |
|---|---|
| Accesso al valore interno | d["outer"]["inner"] |
| Accesso sicuro | d.get("outer", {}).get("inner", default) |
| Aggiunta / aggiornamento chiave interna | d["outer"]["inner"] = value |
| Aggiunta di nuovo sotto-dizionario | d["new_key"] = {...} |
| Eliminazione chiave interna | del d["outer"]["inner"] |
| Eliminazione sotto-dizionario | del d["outer"] |
| Iterazione su tutte le voci | for k, v in d.items(): for ik, iv in v.items(): |
| Copia profonda | import copy; copy.deepcopy(d) |