Articolo originale: The Python Sort List Array Method – Ascending and Descending Explained with Examples

Se vuoi imparare come lavorare con il metodo sort() nei tuoi progetti con Python, allora questo è l'articolo giusto per te. È un metodo molto utile e puoi personalizzarlo a seconda di cosa hai bisogno, dunque vediamo nel dettaglio come funziona.

Imparerai:

  • Come usare questo metodo e personalizzarne le funzionalità.
  • Quando usarlo e quando non usarlo.
  • Come eseguirlo passando diverse combinazioni di argomenti.
  • Come riordinare una lista in ordine crescente o decrescente.
  • Come confrontare gli elementi di una lista basandosi su dei valori intermedi.
  • Come passare delle funzioni lambda nel metodo.
  • Come questo metodo si differenzia dalla funzione sorted().
  • Perché il metodo sort() esegue una riorganizzazione stabile.
  • Come avviene il processo di mutazione dietro le quinte.

Siamo pronti? Cominciamo! ⭐

🔹 Scopo e Casi d'Uso

Con il metodo sort(), puoi riordinare una lista in:

  • Ordine crescente
  • Ordine decrescente

Questo metodo viene usato per riordinare una lista "sul posto"; ciò vuol dire che la modifica direttamente senza crearne copie aggiuntive, quindi ricorda:

image-113
sort() modifica la lista originale

Imparerai di più sulla mutazione delle liste in questo articolo (promesso!), ma per ora è molto importante che tu sappia che il metodo sort() modifica la lista, dunque la versione originale viene persa.

Per questo motivo, dovresti usare questo metodo soltanto se:

  • Vuoi modificare (riordinare) la lista in modo permanente.
  • Non hai bisogno di mantenere la versione originale della lista.

Se queste condizioni si adattano alle tue esigenze, allora il metodo .sort() è esattamente ciò che stai cercando.

🔸 Sintassi e Argomenti

Vediamo come eseguire .sort() per sfruttare appieno le sue potenzialità.

Questo è il modo più basilare per eseguirlo (senza argomenti):

image-41

Se non vuoi passare nessun argomento automaticamente:

  • La lista verrà ordinata in ordine crescente.
  • I valori degli elementi della lista verranno confrontati direttamente usando l'operatore <.

Per esempio:

>>> b = [6, 3, 8, 2, 7, 3, 9]
>>> b.sort()
>>> b
[2, 3, 3, 6, 7, 8, 9] # Riordinata!

Argomenti personalizzati

Per regolare il modo in cui il metodo sort() funziona, puoi passare due argomenti opzionali:

  • Key
  • Reverse

Vediamo in che modo cambiano l'esecuzione del metodo. Qui in basso vediamo un'esecuzione con questi due argomenti:

image-42

Prima di spiegare come funzionano, voglio porre l'attenzione su una cosa che probabilmente hai notato nel diagramma in alto - nella chiamata del metodo, i nomi degli argomenti devono essere inclusi prima dei valori corrispondenti, così:

  • key=<f>
  • reverse=<value>

Questo è perché sono degli argomenti a parole chiave. Se vuoi passare a questi argomenti un valore opzionale, dovrai specificare i loro nomi nella chiamata del metodo, seguiti dal simbolo uguale = e i corrispondenti valori, così:

image-46

In caso contrario, se provassi a passare gli argomenti direttamente come si fa di solito con i parametri posizionali, otterresti questo errore, perché la funzione non sa quali valori corrispondono a quali parametri:

TypeError: sort() takes no positional arguments

Reverse

Adesso che hai imparato cosa sono gli argomenti a parole chiave, cominciamo con reverse.

Il valore di reverse può essere True o False:

  • False significa che la lista verrà riordinata in ordine crescente.
  • True significa che la lista verrà riordinata in ordine decrescente (o inverso).

💡 Attenzione: Il suo valore predefinito è False – se non passi nessun argomento a questo parametro, la lista verrà riordinata in ordine crescente.

Ecco alcuni esempi:

image-123
Di default, il valore di reverse è False
# Lista di numeri interi
>>> b = [6, 3, 8, 2, 7, 3, 9]
>>> b.sort()
>>> b
[2, 3, 3, 6, 7, 8, 9]

# Lista di stringhe
>>> c = ["A", "Z", "D", "T", "U"]
>>> c.sort()
>>> c
['A', 'D', 'T', 'U', 'Z']

💡 Attenzione: Se tutti gli elementi della lista sono stringhe, verranno riordinati in ordine alfabetico.

image-117
Bisogna specificare che reverse è True perché la lista venga riordinata in ordine decrescente.
# Lista di numeri interi
>>> b = [6, 3, 8, 2, 7, 3, 9]
>>> b.sort(reverse=True)
>>> b
[9, 8, 7, 6, 3, 3, 2]

# Lista di stringhe
>>> c = ["A", "Z", "D", "T", "U"]
>>> c.sort(reverse=True)
>>> c
['Z', 'U', 'T', 'D', 'A']

💡 Attenzione: Nota come la lista viene riordinata in ordine inverso se reverse è True.

Key

Adesso che sai come usare il parametro reverse, vediamo il parametro key.

Questo parametro è leggermente più dettagliato, perché determina in che modo gli elementi della lista devono essere confrontati durante il processo di riorganizzazione.

image-120
Sintassi di base

Il valore di key può essere:

  • None, il che significa che gli elementi della lista verranno messi a confronto direttamente. Per esempio, in una lista di numeri interi, i numeri stessi saranno messi a confronto.
  • Una funzione di un argomento che genera un valore intermedio per ogni elemento. Questo valore intermedio viene calcolato solo una volta ed è usato come metro di paragone per tutto il processo di ordinamento. Lo usiamo quando non vogliamo confrontare gli elementi direttamente; per esempio, quando vogliamo paragonare delle stringhe basandoci sulla loro lunghezza (che farà da valore intermedio).

💡 Attenzione: Il valore predefinito di key è None, per cui gli elementi sono messi a confronto direttamente.

Per esempio:

Ipotizziamo di voler riordinare una lista di stringhe basandoci sulla loro lunghezza, dalla più corta alla più lunga. Possiamo passare la funzione len come valore di key, così:

>>> d = ["aaa", "bb", "c"]
>>> d.sort(key=len)
>>> d
['c', 'bb', 'aaa']

💡 Attenzione: Nota che passiamo solamente il nome della funzione (len), senza parentesi perché non la stiamo chiamando. Questo è molto importante.

Nota la differenza tra il mettere a confronto gli elementi direttamente e mettere a confronto la loro lunghezza (immagine in basso). Usando il valore predefinito di key (None) le stringhe sarebbero state riordinate in ordine alfabetico (a sinistra), ma ora le abbiamo riordinate in base alla loro lunghezza (a destra):

image-49

Che cosa avviene dietro le quinte? Ogni elemento viene passato come argomento della funzione len(), e il valore restituito dalla funzione quando viene evocata viene usato per confrontare le stringhe durante il processo di ordinamento:

image-122

In questo modo otteniamo una lista con un criterio di ordinamento diverso: la lunghezza.

Ecco un altro esempio:

Un altro esempio interessante è il mettere in ordine una lista di stringhe come se fossero composte da sole lettere minuscole (per esempio, rendere "Aa" equivalente a "aa").

Stando all'ordine lessicografico, le lettere maiuscole vengono prima delle lettere minuscole:

>>> "E" < "e"
True

Per cui, la stringa "Emma" viene prima di "emily" in una lista ordinata, a prescindere dal fatto che, se fossero entrambe minuscole, andrebbero in ordine opposto:

>>> "Emma" < "emily"
True
>>> "emma" < "emily"
False

Per evitare di dover distinguere tra lettere maiuscole e minuscole, possiamo passare la funzione str.lower come key. In questo modo avremmo una versione in minuscolo delle stringhe che verrà messa a confronto:

>>> e = ["Emma", "emily", "Amy", "Jason"]
>>> e.sort(key=str.lower)
>>> e
['Amy', 'emily', 'Emma', 'Jason']

Nota che adesso "emily" viene prima di "Emma" nella lista ordinata, che è esattamente ciò che volevamo ottenere.

💡 Attenzione: se avessimo usato il metodo di ordinamento predefinito, tutte le stringhe che iniziano con la lettera maiuscola sarebbero venute prima di tutte le stringhe che iniziano con la minuscola:

>>> e = ["Emma", "emily", "Amy", "Jason"]
>>> e.sort()
>>> e
['Amy', 'Emma', 'Jason', 'emily']

Ecco un esempio usando la Programmazione Orientata agli Oggetti:

Consideriamo questa semplice classe in Python:

>>> class Client:
	def __init__(self, age):
		self.age = age

Creiamo quattro istanze:

>>> client1 = Client(67)
>>> client2 = Client(23)
>>> client3 = Client(13)
>>> client4 = Client(35)

Possiamo creare una lista che fa riferimento a queste istanze:

>>> clients = [client1, client2, client3, client4]

Poi, se definiamo una funzione per ottenere la age di queste istanze:

>>> def get_age(client):
	return client.age

Possiamo ordinare la lista basandoci sulle età, passando la funzione get_age come argomento:

>>> clients.sort(key=get_age)

Questa è la versione finale e riordinata della lista. Usiamo un loop for per visualizzare le istanze nell'ordine in cui compaiono nella lista:

>>> for client in clients:
	print(client.age)

	
13
23
35
67

Esattamente ciò che volevamo - adesso la lista è riordinata in ordine crescente basandosi sulle età delle istanze.

💡 Suggerimento: Invece di definire la funzione get_age, avremmo potuto usare una funzione lambda per ottenere le età di ogni istanza, in questo modo:

>>> clients.sort(key=lambda x: x.age)

Le funzioni lambda sono brevi e semplici funzioni anonime, il che significa che non hanno un nome. Sono molto utili nei casi in cui servano funzioni da usare in specifiche righe di codice per un breve periodo di tempo.

Questa è la struttura base della funzione lambda che stiamo usando per riordinare la lista:

image-90
Struttura base di una funzione lambda

Passare entrambi gli argomenti

Eccellente! Ora sai come personalizzare le funzionalità del metodo sort(). Ma puoi portare le tue abilità a un livello superiore combinando gli effetti di key e reverse nella stessa chiamata:

>>> f = ["A", "a", "B", "b", "C", "c"]
>>> f.sort(key=str.lower, reverse=True)
>>> f
['C', 'c', 'B', 'b', 'A', 'a']
Riordina la lista in ordine inverso come se le stringhe fossero tutte in minuscolo

Ecco le diverse combinazioni degli argomenti e dei loro effetti:

image-124
  • key=None, reverse=False: confronta gli elementi in base al loro valore e li riordina in ordine crescente
  • key=None, reverse=True: confronta gli elementi in base al loro valore e li riordina in ordine decrescente
  • key=<funzione>, reverse=False: confronta gli elementi in base al valore restituito dalla funzione e li riordina in ordine crescente
  • key=<funzione>, reverse=True: confronta gli elementi in base al valore restituito dalla funzione e li riordina in ordine decrescente

L'Ordine degli Argomenti a Parole Chiave non Importa

Poiché stiamo specificando i nomi degli argomenti, sappiamo già quali valori corrispondono a quali parametri, quindi possiamo mettere sia key che reverse per primi nella lista e l'effetto sarà identico.

Dunque questa chiamata del metodo:

image-53

È equivalente a:

image-54

Ecco un esempio:

>>> a = ["Zz", "c", "y", "o", "F"]
>>> a.sort(key=str.lower, reverse=True)
>>> a
['Zz', 'y', 'o', 'F', 'c']

Se cambiamo l'ordine degli argomenti, otteniamo lo stesso risultato:

>>> a = ["Zz", "c", "y", "o", "F"]
>>> a.sort(reverse=True, key=str.lower)
>>> a
['Zz', 'y', 'o', 'F', 'c']

🔹 Valore Restituito

Adesso parliamo un po' del valore restituito da questo metodo. Il metodo sort() restituisce Nonenon restituisce una versione riordinata della lista, come avremmo potuto pensare.

Stando alla documentazione di Python:

Per ricordare agli utenti che il metodo è un effetto collaterale, esso non restituisce la sequenza riordinata.

In pratica, questo ci ricorda che stiamo modificando la lista originale salvata in memoria e non stiamo, invece, creando una nuova copia della suddetta lista.

Questo è un esempio del valore restituito da sort():

>>> nums = [6.5, 2.4, 7.3, 3.5, 2.6, 7.4]

# Assegniamo il valore restituito a questa variabile:
>>> val = nums.sort()

# Osserva il valore restituito:
>>> print(val)
None

Vedi? None è stato restituito dall'esecuzione del metodo.

💡 Attenzione: È molto importante non confondere il metodo sort() con la funzione sorted(), la quale agisce in modo molto simile, ma non modifica la lista originale. Al contrario, sorted() genera e restituisce una copia riordinata della lista.

Ecco un esempio per metterli a confronto:

# Il metodo sort() restituisce None
>>> nums = [6.5, 2.4, 7.3, 3.5, 2.6, 7.4]
>>> val = nums.sort()
>>> print(val)
None
Esempio di .sort()
# sorted() restituisce una copia riordinata della lista
>>> nums = [6.5, 2.4, 7.3, 3.5, 2.6, 7.4]
>>> val = sorted(nums)
>>> val
[2.4, 2.6, 3.5, 6.5, 7.3, 7.4]

# Ma non modifica la lista originale!
>>> nums
[6.5, 2.4, 7.3, 3.5, 2.6, 7.4]
Esempio di sorted()

Questo è molto importante perché il loro effetto è molto diverso. Usare sort() al posto di sorted() può causare bug molto problematici nel tuo programma perché potresti non realizzare che la lista viene modificata.

🔸 Il Metodo sort() Riordina in Modo Stabile

Adesso parliamo un po' delle caratteristiche dell'algoritmo di ordinamento usato da sort().

Questo metodo riordina in modo stabile perché lavora con un'implementazione di TimSort, un algoritmo di ordinamento molto efficiente e stabile.

Secondo la documentazione di Python:

Un ordinamento si può definire stabile se garantisce di non cambiare l'ordine relativo degli elementi equivalenti - ciò è utile per riordinare in più passaggi (ad esempio, riordinare prima per dipartimento e poi per stipendio).

Ciò significa che se due elementi hanno lo stesso valore o lo stesso valore intermedio (chiave), rimarranno nello stesso ordine relativo rispetto agli altri.

Vediamo che cosa voglio dire con questo. Dai un'occhiata a questo esempio:

>>> d = ["BB", "AA", "CC", "A", "B", "AAA", "BBB"]
>>> d.sort(key=len)
>>> d
['A', 'B', 'BB', 'AA', 'CC', 'AAA', 'BBB']

Stiamo confrontando degli elementi basandoci sulla loro lunghezza, perché abbiamo passato la funzione len come argomento di key.

Possiamo notare che ci sono due elementi di lunghezza 2: "BB", "AA", e "CC" in questo ordine.

Ora, nota che questi tre elementi sono nello stesso ordine relativo nella lista riordinata finale:

image-92

Questo perché l'algoritmo garantisce stabilità, e i tre elementi avevano lo stesso valore intermedio (chiave) durante il processo di ordinamento (la loro lunghezza è 2, dunque la chiave è 2).

💡 Attenzione: La stessa cosa è successa con "A" e "B" (lunghezza 1), e "AAA" e "BBB" (lunghezza 3), il loro ordine relativo rispetto agli altri è stato mantenuto.

Adesso sai come funziona il metodo sort(), quindi andiamo a vedere come funziona la mutazione e come può influenzare il tuo programma.

🔹 Mutazione e Rischi

Come promesso, vediamo come funziona il processo di mutazione dietro le quinte:

Quando definisci una lista in Python, così:

a = [1, 2, 3, 4]

Stai creando un oggetto in una specifica posizione in memoria. Questa posizione viene chiamata "indirizzo di memoria" dell'oggetto, rappresentato da un numero intero unico chiamato id.

Puoi pensare all'id come ad un'"etichetta" usata per identificare un posto specifico della memoria:

image-40

Puoi accedere all'id di una lista usando la funzione id(), passando la lista come argomento:

>>> a = [1, 2, 3, 4]
>>> id(a)
60501512

Quando muti la lista, la cambi direttamente all'interno della memoria. Potresti chiederti, perché è così rischioso?

È rischioso perché va a influire su ogni singola riga di codice che usa la lista dopo la mutazione, quindi potresti scrivere un codice per lavorare con una lista che è completamente diversa dalla lista presente in memoria dopo la mutazione.

Per questo motivo devi fare molta attenzione con i metodi che causano mutazione.

In particolare, il metodo sort() muta la lista. Questo è un esempio del suo effetto:

image-94

Ecco un esempio:

# Definisci una lista
>>> a = [7, 3, 5, 1]

# Controlla il suo id
>>> id(a)
67091624

# Riordina la lista usando .sort()
>>> a.sort()

# Controlla il suo id (è lo stesso, dunque la lista è lo stesso oggetto all'interno della memoria)
>>> id(a)
67091624

# Ora la lista è riordinata. È stata trasformata!
>>> a
[1, 3, 5, 7]

La lista è stata mutata dopo l'esecuzione di .sort().

Ogni singola riga di codice che lavora con la lista a dopo la mutazione, userà la versione nuova e riordinata della lista. Se non è quello che avevi intenzione di fare, potresti non realizzare che altre parti del tuo programma usano la nuova versione della lista.

Ecco un altro esempio dei rischi della mutazione all'interno di una funzione:

# Lista
>>> a = [7, 3, 5, 1]

# Funzione che visualizza gli elementi della lista in ordine crescente
>>> def print_sorted(x):
	x.sort()
	for elem in x:
		print(elem)

# Chiamiamo la funzione passando 'a' come argomento	
>>> print_sorted(a)
1
3
5
7

# Oops! La lista originale è stata mutata.
>>> a
[1, 3, 5, 7]

La lista a che è stata passata come argomento viene mutata, anche se non era ciò che intendevi fare quando hai scritto la funzione all'inizio.

💡 Attenzione: Se una funzione trasforma un argomento, deve essere dichiarato in modo chiaro per evitare di causare bug in altre parti del tuo programma.

🔸 Riassunto del Metodo sort()

  • Il metodo sort() ti permette di riordinare una lista in ordine crescente o decrescente.
  • Prende due argomenti a parole chiave: key e reverse.
  • reverse determina se la lista deve essere riordinata in ordine crescente o decrescente.
  • key è una funzione che genera un valore intermedio per ogni elemento, e questo valore viene usato per confrontare gli elementi nel processo di ordinamento.
  • Il metodo sort() muta la lista, causando cambiamenti permanenti. Devi fare molta attenzione e usarla solamente se non hai bisogno della versione originale della lista.

Spero davvero che ti sia piaciuto il mio articolo e che tu l'abbia trovato utile. Ora puoi usare il metodo sort() nei tuoi progetti con Python. Dai un'occhiata ai miei corsi online. Seguimi su Twitter. ⭐️