W3docs

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 evitare KeyError
  • 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:

python— editable, runs on the server

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:

python— editable, runs on the server

Aggiunta di Nuovi Sotto-Dizionari

Assegna un nuovo dizionario letterale a una nuova chiave esterna:

python— editable, runs on the server

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: 25

Appiattire 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 assigned

Dizionari 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"])  # Alice

Questo è 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 safe

Consulta 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

OperazioneSintassi
Accesso al valore internod["outer"]["inner"]
Accesso sicurod.get("outer", {}).get("inner", default)
Aggiunta / aggiornamento chiave internad["outer"]["inner"] = value
Aggiunta di nuovo sotto-dizionariod["new_key"] = {...}
Eliminazione chiave internadel d["outer"]["inner"]
Eliminazione sotto-dizionariodel d["outer"]
Iterazione su tutte le vocifor k, v in d.items(): for ik, iv in v.items():
Copia profondaimport copy; copy.deepcopy(d)

Esercitazione

Pratica
Quale metodo legge in modo sicuro una chiave mancante da un dizionario annidato senza generare un KeyError?
Quale metodo legge in modo sicuro una chiave mancante da un dizionario annidato senza generare un KeyError?
Was this page helpful?