← Tutte le guide

Python: ordinare con `sorted`, `key` e `itemgetter` (e perché l'ordinamento è stabile)

29 ottobre 2026 · 2 min di lettura

Ordinare una lista di dizionari per uno o più campi è un'operazione quotidiana. Python la rende semplice, ma ci sono due o tre dettagli che conviene conoscere.

key, non confronti

sorted() accetta key: una funzione applicata una sola volta a ogni elemento, il cui risultato viene usato per confrontare.

from operator import itemgetter, attrgetter

righe = [{"nome": "Anna", "eta": 31}, {"nome": "Bruno", "eta": 25}]

sorted(righe, key=lambda r: r["eta"])
sorted(righe, key=itemgetter("eta"))      # equivalente, un po' più veloce
# per gli oggetti: attrgetter("eta")

L'ordinamento è stabile

A parità di chiave, gli elementi restano nell'ordine originale. Vale anche con reverse=True: l'ordine degli elementi uguali non si inverte.

dati = [("b", 1), ("a", 1), ("c", 0), ("d", 1)]
sorted(dati, key=itemgetter(1))
# [('c', 0), ('b', 1), ('a', 1), ('d', 1)]
sorted(dati, key=itemgetter(1), reverse=True)
# [('b', 1), ('a', 1), ('d', 1), ('c', 0)]

Più criteri

Con criteri tutti nello stesso verso basta una tupla (o itemgetter("eta", "nome")). Con versi diversi hai due strade, verificate uguali sui dati di prova:

# 1) chiave con il segno meno (solo per numeri)
sorted(righe, key=lambda r: (-r["eta"], r["nome"]))

# 2) due passaggi, sfruttando la stabilità: prima il criterio secondario
a = sorted(righe, key=itemgetter("nome"))
a = sorted(a, key=itemgetter("eta"), reverse=True)

Il secondo funziona anche con stringhe in verso inverso, dove il segno meno non si può usare.

Trappola: None nei dati

sorted([3, None, 1])
# TypeError: '<' not supported between instances of 'NoneType' and 'int'

Soluzione: includere nella chiave se il valore manca.

sorted([3, None, 1], key=lambda x: (x is None, x if x is not None else 0))
# [1, 3, None]

Quanto costa

100.000 dizionari, Python 3.12.15, minimo di 5 prove:

  • lambda r: r["eta"]: ~9,5 ms
  • itemgetter("eta"): ~7,9 ms
  • itemgetter("eta", "nome"): ~34,5 ms
  • due passaggi con itemgetter: ~24,6 ms
  • tupla con (-eta, nome) via lambda: ~43,1 ms
  • cmp_to_key con una funzione di confronto: ~79,8 ms
  • list.sort() e sorted() sono pari (~8,3 ms): sort risparmia la copia di memoria, non il tempo.
  • Rifare il sort su una lista già ordinata: ~2,7 ms contro ~7,9 ms su una casuale (Timsort sfrutta i tratti già ordinati).

Quindi: itemgetter quando basta, i due passaggi sono spesso più veloci di una tupla costruita con lambda, e cmp_to_key solo se non c'è alternativa.

Il codice completo, eseguito per scrivere questo articolo, è nel nostro repository di verifica (sorted_key.py). Le misure dipendono dalla macchina: contano i rapporti, non i millisecondi.