Python & AI Tutorials Logo
Programmazione Python

37. Funzioni integrate e strumenti utili

Python offre una ricca raccolta di funzioni integrate sempre disponibili senza bisogno di importare alcun modulo. Queste funzioni costituiscono la base della programmazione Python di tutti i giorni, aiutandoti a lavorare in modo efficiente con dati, sequenze e collezioni. In questo capitolo esploreremo gli strumenti integrati più utili di Python e impareremo come sfruttarli per scrivere codice più pulito e più espressivo.

Comprendere il sistema di tipi di Python

Prima di approfondire funzioni integrate specifiche, è utile capire come Python organizza i suoi tipi di dati. Questa conoscenza ti aiuterà a prevedere quali operazioni funzionano con quali tipi e a comprendere i messaggi di errore quando si verificano.

I tipi di dati di Python possono essere compresi da due prospettive complementari:

Gerarchia dei tipi: come i tipi sono correlati

object

Tipi numerici

Tipi sequenza

Tipi mapping

Tipi set

Altro

int

float

complex

Immutabili

Mutabili

str

tuple

range

list

dict

set

frozenset

bool

NoneType

Questo mostra come Python organizza i tipi in famiglie in base a ciò che SONO.

Vista basata sulle capacità: cosa possono fare i tipi

Per le funzioni integrate, ciò che conta di più è cosa i tipi possono FARE:

Tutti gli oggetti Python

Iterable
(si può ciclare con for)

Non iterable
(int, float, bool)

Collection
(ha una lunghezza)

Altri iterable
(generator, iterator)

Sequence
(ordinata, indicizzata)

Non ordinata
(set, dict)

str

list

tuple

range

set

dict

Capacità chiave:

  • Iterable: Può essere usato nei cicli(loop) for → Funziona con sum(), any(), all(), sorted()
  • Collection: Iterable con len() → Funziona con len() e con l’operatore in
  • Sequence: Collection con indicizzazione → Supporta [index] e lo slicing [start:end]

Perché questo è importante

Le funzioni integrate richiedono capacità specifiche:

FunzioneRichiedeFunziona con
len()Collectionstr, list, dict, set, tuple
sum()Iterable di numerilist, tuple, set, range, generator
sorted()Iterablestr, list, dict, set, tuple
[index]Sequencestr, list, tuple, range

Comprendere queste categorie ti aiuta a:

  • Prevedere quali funzioni funzionano con quali tipi
  • Comprendere messaggi di errore come "object is not iterable"
  • Sapere quando puoi indicizzare ([0]) rispetto a quando puoi solo ciclare (for)

37.1) Funzioni integrate comuni (len, sum, min, max, abs, round)

Le funzioni integrate più usate di Python ti aiutano a eseguire operazioni comuni sui dati senza scrivere cicli(loop) o logiche complesse. Queste funzioni sono ottimizzate, leggibili e costituiscono la base del codice Pythonic.

37.1.1) Misurare la lunghezza con len()

La funzione len() restituisce il numero di elementi in una collezione. Funziona con stringhe, liste(list), tuple, dizionari, set e qualsiasi altro tipo di collezione.

python
# Contare i caratteri in una stringa
message = "Hello, World!"
print(len(message))  # Output: 13
 
# Contare gli elementi in una lista
scores = [85, 92, 78, 90, 88]
print(len(scores))   # Output: 5
 
# Contare le coppie chiave-valore in un dizionario
student = {"name": "Bob", "age": 21, "major": "CS"}
print(len(student))  # Output: 3
 
# Contare gli elementi unici in un set
unique_ids = {101, 102, 103, 101, 102}  # Duplicati rimossi
print(len(unique_ids))  # Output: 3

La funzione len() è particolarmente utile quando devi conoscere la dimensione dei dati prima di elaborarli:

python
# Elaborare i dati in base alla dimensione
data = [12, 45, 23, 67, 89, 34]
 
if len(data) < 5:
    print("Not enough data for analysis")
else:
    print(f"Analyzing {len(data)} data points")  # Output: Analyzing 6 data points
    average = sum(data) / len(data)
    print(f"Average: {average}")  # Output: Average: 45.0

37.1.2) Calcolare i totali con sum()

La funzione sum() somma tutti i numeri in un iterable. È molto più pulita rispetto a scrivere un ciclo(loop) per accumulare valori.

python
# Sommare una lista di numeri
prices = [19.99, 24.50, 15.75, 32.00]
total = sum(prices)
print(f"Total: ${total}")  # Output: Total: $92.24
 
# Sommare una tupla
daily_steps = (8500, 10200, 7800, 9500, 11000)
weekly_total = sum(daily_steps)
print(f"Total steps this week: {weekly_total}")  # Output: Total steps this week: 47000
 
# Sommare un range
total_1_to_100 = sum(range(1, 101))
print(total_1_to_100)  # Output: 5050

Un esempio pratico che combina sum() e len() per calcolare le medie:

python
# Calcolare il punteggio medio dei test
test_scores = [88, 92, 79, 85, 90, 87]
 
total_score = sum(test_scores)
num_tests = len(test_scores)
average_score = total_score / num_tests
 
print(f"Average score: {average_score:.1f}")  # Output: Average score: 86.8

Limitazione importante: sum() funziona solo con numeri. Non puoi usarla per concatenare stringhe o combinare liste:

python
# Questo solleva TypeError
words = ["Hello", " ", "World"]
# sentence = sum(words)  # TypeError: unsupported operand type(s)

37.1.3) Trovare estremi con min() e max()

Le funzioni min() e max() trovano i valori più piccolo e più grande in un iterable. Funzionano con numeri, stringhe e qualsiasi oggetto che possa essere confrontato.

python
# Trovare numeri minimi e massimi
temperatures = [72, 68, 75, 70, 73, 69]
coldest = min(temperatures)
warmest = max(temperatures)
print(f"Temperature range: {coldest}°F to {warmest}°F")
# Output: Temperature range: 68°F to 75°F
 
# Trovare stringhe minime e massime (in ordine alfabetico)
names = ["Zoe", "Alice", "Bob", "Charlie"]
first_alphabetically = min(names)
last_alphabetically = max(names)
print(f"First: {first_alphabetically}, Last: {last_alphabetically}")
# Output: First: Alice, Last: Zoe

Puoi anche passare più argomenti direttamente invece di una collezione:

python
# Confrontare valori singoli
lowest = min(45, 23, 67, 12, 89)
highest = max(45, 23, 67, 12, 89)
print(f"Lowest: {lowest}, Highest: {highest}")
# Output: Lowest: 12, Highest: 89
 
# Utile per confrontare alcuni valori specifici
price1 = 19.99
price2 = 24.50
price3 = 15.75
cheapest = min(price1, price2, price3)
print(f"Cheapest option: ${cheapest}")  # Output: Cheapest option: $15.75

37.1.4) Ottenere valori assoluti con abs()

La funzione abs() restituisce il valore assoluto di un numero—la sua distanza da zero, sempre positiva. È utile quando ti interessa la magnitudine ma non la direzione.

python
# Valore assoluto di numeri negativi
print(abs(-42))      # Output: 42
print(abs(-3.14))    # Output: 3.14
 
# Valore assoluto di numeri positivi (invariato)
print(abs(42))       # Output: 42
print(abs(3.14))     # Output: 3.14
 
# Valore assoluto di zero
print(abs(0))        # Output: 0

Un caso d’uso comune è calcolare differenze dove la direzione non conta:

python
# Calcolare il cambiamento di temperatura (solo magnitudine)
morning_temp = 65
evening_temp = 72
temperature_change = abs(evening_temp - morning_temp)
print(f"Temperature changed by {temperature_change}°F")
# Output: Temperature changed by 7°F

37.1.5) Arrotondare numeri con round()

La funzione round() arrotonda un numero a un numero specificato di cifre decimali. Senza il secondo argomento, arrotonda all’intero più vicino.

python
# Arrotondare all'intero più vicino
print(round(3.7))     # Output: 4
print(round(3.2))     # Output: 3
print(round(3.5))     # Output: 4
print(round(4.5))     # Output: 4  (arrotonda al numero pari più vicino)
 
# Arrotondare a un numero specifico di cifre decimali
price = 19.876
print(round(price, 2))  # Output: 19.88  (2 cifre decimali)
print(round(price, 1))  # Output: 19.9   (1 cifra decimale)
 
# Arrotondare a cifre decimali negative (arrotonda a decine, centinaia, ecc.)
population = 1234567
print(round(population, -3))  # Output: 1235000  (migliaio più vicino)
print(round(population, -4))  # Output: 1230000  (diecimila più vicino)

Nota sui valori a metà: Quando arrotondi un numero che è esattamente a metà tra due interi, Python ha una regola speciale. Per esempio, 2.5 è esattamente a metà tra 2 e 3. Potresti aspettarti che arrotondi a 3, ma Python arrotonda al vicino che è un numero pari—in questo caso, 2.

Questo si chiama "banker's rounding" o "round half to even." Fa parte dello standard IEEE 754 e aiuta a ridurre il bias su molte operazioni di arrotondamento.

python
# I valori a metà arrotondano al numero pari più vicino
print(round(0.5))   # Output: 0 (0 è pari)
print(round(1.5))   # Output: 2 (2 è pari)
print(round(2.5))   # Output: 2 (2 è pari)
print(round(3.5))   # Output: 4 (4 è pari)
print(round(4.5))   # Output: 4 (4 è pari)

37.2) Enumerare sequenze con enumerate()

Quando cicli(loop) su una sequenza, spesso ti servono sia l’elemento sia la sua posizione. La funzione enumerate() fornisce entrambe le cose, eliminando la necessità di variabili contatore manuali.

37.2.1) Il problema dei contatori manuali

Prima di conoscere enumerate(), i programmatori spesso usano una variabile contatore per tenere traccia della posizione:

python
# Approccio con contatore manuale (funziona ma non è ideale)
fruits = ["apple", "banana", "cherry", "date"]
index = 0
 
for fruit in fruits:
    print(f"{index}: {fruit}")
    index += 1
# Output:
# 0: apple
# 1: banana
# 2: cherry
# 3: date

Questo approccio ha diversi svantaggi:

  • Variabile extra da gestire (index)
  • È facile dimenticarsi di incrementare il contatore

37.2.2) Usare enumerate() per posizione e valore

La funzione enumerate() risolve questo problema in modo elegante. Prende un iterable e restituisce coppie di (index, element):

python
# Usare enumerate() - più pulito e più Pythonic
fruits = ["apple", "banana", "cherry", "date"]
 
for index, fruit in enumerate(fruits):
    print(f"{index}: {fruit}")
# Output:
# 0: apple
# 1: banana
# 2: cherry
# 3: date

La sintassi for index, fruit in enumerate(fruits) usa l’unpacking di tuple (come abbiamo visto nel Capitolo 15). A ogni iterazione, enumerate() fornisce una tupla come (0, "apple"), che viene spacchettata nelle variabili index e fruit.

Ecco cosa produce effettivamente enumerate():

python
# Vedere direttamente l'output di enumerate
fruits = ["apple", "banana", "cherry"]
enumerated = list(enumerate(fruits))
print(enumerated)
# Output: [(0, 'apple'), (1, 'banana'), (2, 'cherry')]

37.2.3) Iniziare l'enumerazione da un numero diverso

Per impostazione predefinita, enumerate() inizia a contare da 0. Puoi specificare un numero iniziale diverso con il parametro start:

python
# Iniziare a contare da 1 (utile per la visualizzazione)
tasks = ["Write code", "Test code", "Deploy code"]
 
for number, task in enumerate(tasks, start=1):
    print(f"Step {number}: {task}")
# Output:
# Step 1: Write code
# Step 2: Test code
# Step 3: Deploy code

Questo è particolarmente utile quando mostri elenchi numerati agli utenti, che in genere si aspettano che il conteggio inizi da 1:

python
# Menu con opzioni numerate
menu_items = ["New Game", "Load Game", "Settings", "Quit"]
 
print("Main Menu:")
for number, item in enumerate(menu_items, start=1):
    print(f"{number}. {item}")
# Output:
# Main Menu:
# 1. New Game
# 2. Load Game
# 3. Settings
# 4. Quit

37.2.4) enumerate() con stringhe e altri iterable

La funzione enumerate() funziona con qualsiasi iterable, non solo con le liste:

python
# Enumerare i caratteri in una stringa
word = "Python"
 
for position, letter in enumerate(word):
    print(f"Letter {position}: {letter}")
# Output:
# Letter 0: P
# Letter 1: y
# Letter 2: t
# Letter 3: h
# Letter 4: o
# Letter 5: n
 
# Enumerare una tupla
coordinates = (10, 20, 30, 40)
 
for index, value in enumerate(coordinates):
    print(f"Coordinate {index}: {value}")
# Output:
# Coordinate 0: 10
# Coordinate 1: 20
# Coordinate 2: 30
# Coordinate 3: 40

La funzione enumerate() rende il codice più leggibile e meno soggetto a errori. Ogni volta che ti servono sia la posizione sia il valore in un ciclo(loop), scegli enumerate() invece di gestire manualmente un contatore.

37.3) Combinare sequenze con zip()

La funzione zip() combina più iterable elemento per elemento, creando coppie (o tuple) di elementi corrispondenti. È preziosissima quando devi elaborare contemporaneamente dati correlati provenienti da sequenze separate.

37.3.1) Comprendere come funziona zip()

La funzione zip() prende due o più iterable e restituisce un iteratore di tuple, dove ogni tupla contiene un elemento da ciascun iterable di input:

python
# Combinare due liste
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
 
combined = list(zip(names, ages))
print(combined)
# Output: [('Alice', 25), ('Bob', 30), ('Charlie', 35)]

Il nome "zip" viene dalla cerniera dei vestiti—unisce due lati separati in una struttura unica, elemento per elemento.

Ecco una rappresentazione visiva di come zip() abbina gli elementi:

names
['Alice', 'Bob', 'Charlie']

zip()

ages
[25, 30, 35]

[('Alice', 25),
('Bob', 30),
('Charlie', 35)]

37.3.2) Usare zip() nei cicli

L’uso più comune di zip() è nei cicli(loop) for, quando devi iterare su più sequenze contemporaneamente:

python
# Elaborare dati paralleli
students = ["Alice", "Bob", "Charlie", "Diana"]
scores = [92, 85, 88, 95]
 
for student, score in zip(students, scores):
    print(f"{student} scored {score}")
# Output:
# Alice scored 92
# Bob scored 85
# Charlie scored 88
# Diana scored 95

Questo è molto più pulito rispetto a usare gli indici:

python
# Senza zip() - più complesso e soggetto a errori
students = ["Alice", "Bob", "Charlie", "Diana"]
scores = [92, 85, 88, 95]
 
for i in range(len(students)):
    print(f"{students[i]} scored {scores[i]}")
# Stesso output, ma più codice e potenziale per errori di indice

37.3.3) Gestire sequenze di lunghezze diverse

Quando le sequenze in input hanno lunghezze diverse, zip() si ferma quando la sequenza più corta è esaurita:

python
# Sequenze di lunghezza non uguale
names = ["Alice", "Bob", "Charlie", "Diana"]
ages = [25, 30]  # Solo 2 età
 
for name, age in zip(names, ages):
    print(f"{name} is {age} years old")
# Output:
# Alice is 25 years old
# Bob is 30 years old
# (Charlie e Diana non vengono elaborati)

Questo comportamento evita errori ma può portare a una perdita silenziosa di dati se non fai attenzione. Verifica sempre che le tue sequenze abbiano le lunghezze attese:

python
# Verificare una mancata corrispondenza di lunghezza
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30]
 
if len(names) != len(ages):
    print(f"Warning: {len(names)} names but {len(ages)} ages")
    print("Only processing the first", min(len(names), len(ages)), "entries")
    # Output: Warning: 3 names but 2 ages
    # Output: Only processing the first 2 entries
 
# Continuare con zip() - si fermerà alla sequenza più corta
for name, age in zip(names, ages):
    print(f"{name} is {age} years old")

37.3.4) Zippare più di due sequenze

La funzione zip() può combinare un numero qualsiasi di iterable:

python
# Combinare tre sequenze
products = ["Laptop", "Mouse", "Keyboard"]
prices = [999.99, 24.99, 79.99]
quantities = [5, 20, 15]
 
print("Inventory Report:")
for product, price, quantity in zip(products, prices, quantities):
    total_value = price * quantity
    print(f"{product}: ${price} × {quantity} = ${total_value:.2f}")
# Output:
# Inventory Report:
# Laptop: $999.99 × 5 = $4999.95
# Mouse: $24.99 × 20 = $499.80
# Keyboard: $79.99 × 15 = $1199.85

37.3.5) Creare dizionari con zip()

Un pattern potente è usare zip() per creare dizionari da sequenze separate di chiavi e valori:

python
# Creare un dizionario da due liste
keys = ["name", "age", "city"]
values = ["Alice", 25, "Boston"]
 
person = dict(zip(keys, values))
print(person)
# Output: {'name': 'Alice', 'age': 25, 'city': 'Boston'}

37.4) Aggregazione booleana con any() e all()

Le funzioni any() e all() testano condizioni su interi iterable, restituendo un singolo risultato booleano. Sono strumenti potenti per validazione e decisioni basate su condizioni multiple.

37.4.1) Comprendere any(): True se almeno un elemento è True

La funzione any() restituisce True se almeno un elemento in un iterable è truthy (valutato come True). Se tutti gli elementi sono falsy, restituisce False:

python
# Esempi base di any()
print(any([True, False, False]))   # Output: True  (almeno un True)
print(any([False, False, False]))  # Output: False (tutti False)
print(any([False, True, True]))    # Output: True  (più valori True)
 
# Iterable vuoti
print(any([]))  # Output: False (nessun elemento che possa essere True)

La funzione any() usa le regole di truthiness di Python (come abbiamo visto nel Capitolo 7). Numeri diversi da zero, stringhe non vuote e collezioni non vuote sono truthy:

python
# any() con diversi valori truthy/falsy
print(any([0, 0, 1]))           # Output: True  (1 è truthy)
print(any([0, 0, 0]))           # Output: False (tutti gli zeri sono falsy)
print(any(["", "", "text"]))    # Output: True  ("text" è truthy)
print(any(["", "", ""]))        # Output: False (le stringhe vuote sono falsy)

37.4.2) Usi pratici di any()

Esempio: verificare se una qualunque condizione è soddisfatta

python
# Verificare se almeno un punteggio è insufficiente (sotto 60)
scores = [75, 82, 55, 90, 88]
has_failing_grade = any(score < 60 for score in scores)
 
if has_failing_grade:
    print("Warning: At least one failing grade")
    # Output: Warning: At least one failing grade
else:
    print("All grades are passing")

37.4.3) Comprendere all(): True solo se tutti gli elementi sono True

La funzione all() restituisce True solo se tutti gli elementi in un iterable sono truthy. Se un qualsiasi elemento è falsy, restituisce False:

python
# Esempi base di all()
print(all([True, True, True]))    # Output: True  (tutti True)
print(all([True, False, True]))   # Output: False (un False)
print(all([True, True, False]))   # Output: False (un False)
 
# Iterable vuoti
print(all([]))  # Output: True (verità vacua - nessun elemento False)

Il comportamento con iterable vuoti potrebbe sembrare sorprendente: all([]) restituisce True. Questo si chiama verità vacua—l’affermazione "tutti gli elementi sono True" è tecnicamente vera quando non ci sono elementi che la contraddicono.

python
# all() con diversi valori truthy/falsy
print(all([1, 2, 3]))           # Output: True  (tutti diversi da zero)
print(all([1, 0, 3]))           # Output: False (0 è falsy)
print(all(["a", "b", "c"]))     # Output: True  (tutte non vuote)
print(all(["a", "", "c"]))      # Output: False (stringa vuota è falsy)

37.4.4) Usi pratici di all()

Esempio: validare che tutte le condizioni siano soddisfatte

python
# Verificare se tutti i punteggi sono sufficienti (60 o più)
scores = [75, 82, 68, 90, 88]
all_passing = all(score >= 60 for score in scores)
 
if all_passing:
    print("Congratulations! All grades are passing")
    # Output: Congratulations! All grades are passing
else:
    print("Some grades need improvement")

37.4.5) Valutazione short-circuit in any() e all()

Sia any() sia all() usano la valutazione short-circuit (come abbiamo visto nel Capitolo 9). Smettono di verificare non appena il risultato è determinato:

python
# Funzione che stampa quando viene chiamata (per mostrare l'esecuzione)
def is_positive(n):
    print(f"Checking {n}")
    return n > 0
 
# any() si ferma al primo True
print("Testing any():")
numbers = [0, 0, 1, 2, 3]
result = any(is_positive(n) for n in numbers)
# Output:
# Testing any():
# Checking 0
# Checking 0
# Checking 1
# (Stops here - doesn't check 2 or 3)
print(f"Result: {result}")  # Output: Result: True
 
print("\nTesting all():")
numbers = [1, 2, 0, 3, 4]
result = all(is_positive(n) for n in numbers)
# Output:
# Testing all():
# Checking 1
# Checking 2
# Checking 0
# (Stops here - doesn't check 3 or 4)
print(f"Result: {result}")  # Output: Result: False

Questo rende any() e all() efficienti—non perdono tempo a verificare elementi dopo che il risultato è stato determinato.

37.5) Ordinare con sorted() e chiavi personalizzate

La funzione sorted() crea una nuova lista ordinata a partire da qualsiasi iterable. A differenza del metodo .sort() (che funziona solo sulle liste e le modifica in-place), sorted() funziona con qualsiasi iterable e restituisce sempre una nuova lista.

37.5.1) Ordinamento di base con sorted()

La funzione sorted() dispone gli elementi in ordine crescente per impostazione predefinita:

python
# Ordinare numeri
numbers = [42, 17, 93, 8, 55]
sorted_numbers = sorted(numbers)
print(sorted_numbers)  # Output: [8, 17, 42, 55, 93]
 
# La lista originale non cambia
print(numbers)  # Output: [42, 17, 93, 8, 55]
 
# Ordinare stringhe (alfabeticamente)
names = ["Charlie", "Alice", "Bob", "Diana"]
sorted_names = sorted(names)
print(sorted_names)  # Output: ['Alice', 'Bob', 'Charlie', 'Diana']

La funzione sorted() funziona con qualsiasi iterable, non solo con le liste:

python
# Ordinare una tupla (restituisce una lista)
coordinates = (5, 2, 8, 1, 9)
sorted_coords = sorted(coordinates)
print(sorted_coords)  # Output: [1, 2, 5, 8, 9]
 
# Ordinare una stringa (restituisce una lista di caratteri)
word = "python"
sorted_letters = sorted(word)
print(sorted_letters)  # Output: ['h', 'n', 'o', 'p', 't', 'y']
 
# Ordinare un set (restituisce una lista ordinata)
unique_numbers = {5, 8, 2, 1}
sorted_unique = sorted(unique_numbers)
print(sorted_unique)  # Output: [1, 2, 5, 8]

37.5.2) Ordinamento inverso

Usa il parametro reverse=True per ordinare in ordine decrescente:

python
# Ordine decrescente per numeri
scores = [85, 92, 78, 95, 88]
highest_first = sorted(scores, reverse=True)
print(highest_first)  # Output: [95, 92, 88, 85, 78]
 
# Ordine decrescente per stringhe (alfabetico inverso)
names = ["Charlie", "Alice", "Bob", "Diana"]
reverse_alpha = sorted(names, reverse=True)
print(reverse_alpha)  # Output: ['Diana', 'Charlie', 'Bob', 'Alice']

37.5.3) Comprendere il parametro key

Il parametro key è dove sorted() diventa davvero potente. Trasforma il modo in cui Python confronta gli elementi durante l’ordinamento.

Che cos’è il parametro key?

Il parametro key accetta una funzione(function). Python chiama questa funzione su ciascun elemento per estrarre una "chiave di confronto", poi ordina in base a queste chiavi invece che in base agli elementi originali.

Come funziona passo per passo:

  1. Python chiama la funzione key su ciascun elemento
  2. Python raccoglie tutte le chiavi
  3. Python ordina confrontando queste chiavi
  4. Python restituisce gli elementi originali nel nuovo ordine
python
# Esempio: ordinare per lunghezza
words = ["python", "is", "awesome"]
 
# Passo 1: Python chiama len() su ogni parola
# len("python")  → 6
# len("is")      → 2
# len("awesome") → 7
 
# Passo 2: Python ha queste chiavi: [6, 2, 7]
 
# Passo 3: Python ordina le chiavi: [2, 6, 7]
 
# Passo 4: Python restituisce words in quell'ordine: ["is", "python", "awesome"]
 
result = sorted(words, key=len)
print(result)  # Output: ['is', 'python', 'awesome']

Visualizzare la funzione key:

Originale:
['python', 'is', 'awesome']

Applica key=len

Chiavi:
[6, 2, 7]

Ordina le chiavi

Chiavi ordinate:
[2, 6, 7]

Restituisce gli elementi:
['is', 'python', 'awesome']

Cosa può essere una funzione key?

La funzione key deve:

  • Accettare un argomento (l’elemento che viene ordinato)
  • Restituire un valore che Python può confrontare (numeri, stringhe, tuple, ecc.)
python
# Le funzioni integrate vanno benissimo
sorted(numbers, key=abs)        # Ordinare per valore assoluto
sorted(words, key=len)          # Ordinare per lunghezza
sorted(names, key=str.lower)    # Ordinare ignorando maiuscole/minuscole
 
# Le tue funzioni
def first_letter(word):
    return word[0]
 
sorted(words, key=first_letter)  # Ordinare per prima lettera
 
# Funzioni lambda (Capitolo 23)
sorted(words, key=lambda w: w[-1])  # Ordinare per ultima lettera

Importante: la funzione key viene chiamata una volta per elemento

python
# Mostrare quando viene chiamata la funzione key
def show_key(word):
    print(f"Getting key for: {word}")
    return len(word)
 
words = ["cat", "elephant", "dog"]
result = sorted(words, key=show_key)
# Output:
# Getting key for: cat
# Getting key for: elephant
# Getting key for: dog
 
print(result)  # Output: ['cat', 'dog', 'elephant']

Importante: la funzione key viene chiamata una volta per elemento

Nota che show_key viene chiamata esattamente una volta per ogni parola, non ripetutamente durante i confronti. Python è efficiente—estrae prima tutte le chiavi, le mette in cache, poi ordina usando le chiavi in cache.

Pensa a key come alla risposta a: "Quale aspetto devo confrontare?"

  • key=len → "Confronta per lunghezza"
  • key=abs → "Confronta per valore assoluto"
  • key=str.lower → "Confronta come se fosse tutto minuscolo"
  • key=lambda x: x[1] → "Confronta per secondo elemento"

Il parametro key ti consente di ordinare in base a qualsiasi proprietà dei tuoi elementi, rendendo sorted() incredibilmente versatile.

37.5.4) Ordinare usando funzioni integrate come key

Le funzioni integrate di Python sono ottime funzioni key:

python
# Ordinare per valore assoluto
numbers = [-5, 2, -8, 1, -3, 7]
sorted_by_magnitude = sorted(numbers, key=abs)
print(sorted_by_magnitude)  # Output: [1, 2, -3, -5, 7, -8]
 
# Ordinare stringhe ignorando maiuscole/minuscole
names = ["alice", "Bob", "CHARLIE", "diana"]
sorted_case_insensitive = sorted(names, key=str.lower)
print(sorted_case_insensitive)  # Output: ['alice', 'Bob', 'CHARLIE', 'diana']

37.5.5) Ordinare strutture dati complesse

Quando ordini liste di tuple o liste, puoi usare l’indicizzazione per specificare quale elemento usare per ordinare:

python
# Ordinare tuple per il secondo elemento
students = [
    ("Alice", 92),
    ("Bob", 85),
    ("Charlie", 88),
    ("Diana", 95)
]
 
# Ordina per punteggio (secondo elemento)
by_score = sorted(students, key=lambda student: student[1])
print(by_score)
# Output: [('Bob', 85), ('Charlie', 88), ('Alice', 92), ('Diana', 95)]
 
# Ordina per punteggio decrescente
by_score_desc = sorted(students, key=lambda student: student[1], reverse=True)
print(by_score_desc)
# Output: [('Diana', 95), ('Alice', 92), ('Charlie', 88), ('Bob', 85)]

Nota: Qui stiamo usando lambda (come abbiamo visto nel Capitolo 23). Una lambda è una piccola funzione anonima. L’espressione lambda student: student[1] crea una funzione che prende una tupla student e restituisce il suo secondo elemento (il punteggio).

37.5.6) Ordinamento multi-livello

Puoi ordinare per più criteri restituendo una tupla dalla funzione key. Python confronta le tuple elemento per elemento, da sinistra a destra:

Come funziona il confronto tra tuple:

Quando Python confronta due tuple, segue queste regole:

  1. Confronta i primi elementi. Se sono diversi, il confronto è concluso.
  2. Se i primi elementi sono uguali, confronta i secondi elementi.
  3. Se i secondi elementi sono uguali, confronta i terzi elementi.
  4. Continua finché non trova una differenza o finché non finiscono gli elementi.
python
# Esempi di confronto tra tuple
print((1, 'a') < (2, 'z'))   # Output: True  (1 < 2, quindi True immediatamente)
print((1, 'z') < (1, 'a'))   # Output: False (1 == 1, quindi confronta 'z' < 'a')
print((1, 'a') < (1, 'a'))   # Output: False (entrambe le tuple sono uguali)
print((1, 2, 9) < (1, 3, 1)) # Output: True  (1 == 1, poi 2 < 3)

Questo rende le tuple perfette per l’ordinamento multi-livello—Python gestisce automaticamente la logica "confronta il primo criterio, poi il secondo, poi il terzo" al posto tuo:

python
# Ordinare per più criteri
students = [
    ("Alice", "Smith", 92),
    ("Bob", "Jones", 85),
    ("Alice", "Brown", 88),
    ("Charlie", "Smith", 85)
]
 
# Ordina per nome, poi cognome
by_name = sorted(students, key=lambda s: (s[0], s[1]))
print("By name:")
for student in by_name:
    print(f"  {student}")
# Output:
# By name:
#   ('Alice', 'Brown', 88)
#   ('Alice', 'Smith', 92)
#   ('Bob', 'Jones', 85)
#   ('Charlie', 'Smith', 85)
 
# Ordina per punteggio decrescente, poi nome crescente
by_score_then_name = sorted(students, key=lambda s: (-s[2], s[0]))
print("\nBy score (high to low), then name:")
for student in by_score_then_name:
    print(f"  {student}")
# Output:
# By score (high to low), then name:
#   ('Alice', 'Smith', 92)
#   ('Alice', 'Brown', 88)
#   ('Bob', 'Jones', 85)
#   ('Charlie', 'Smith', 85)

Nota: Per ordinare un criterio in modo decrescente e un altro in modo crescente, neghiamo il valore numerico (-s[2]). Questo funziona perché la negazione inverte l’ordine di ordinamento per i numeri. Nell’esempio sopra, -s[2] ordina i punteggi dal più alto al più basso, mentre s[0] ordina i nomi dalla A alla Z.

37.5.7) Usare funzioni di supporto per chiavi complesse

Quando la logica di ordinamento diventa complessa, definire una funzione di supporto rende il codice più leggibile e manutenibile. Puoi poi usare questa funzione di supporto dentro la tua funzione key.

Esempio: ordinare file per estensione

Supponiamo che tu voglia raggruppare i file per la loro estensione (.csv, .jpg, .pdf, ecc.) e, all’interno di ogni gruppo, ordinare alfabeticamente per nome file. La funzione key deve estrarre l’estensione del file, il che richiede un po’ di manipolazione di stringhe.

python
# Ordinare i file per estensione, poi per nome
files = [
    "report.pdf",
    "data.csv",
    "image.jpg",
    "notes.txt",
    "backup.csv",
    "photo.jpg"
]
 
# Estrarre l'estensione per l'ordinamento
def get_extension(filename):
    """Extract the file extension from a filename."""
    return filename.split(".")[-1]  # Dividere per "." e prendere l'ultima parte
 
# Usare la funzione di supporto nella key
sorted_files = sorted(files, key=lambda f: (get_extension(f), f))
print("Files sorted by extension, then name:")
for file in sorted_files:
    print(f"  {file}")
# Output:
# Files sorted by extension, then name:
#   backup.csv      # csv files first (alphabetically)
#   data.csv        # csv files first (alphabetically)
#   image.jpg       # jpg files next
#   photo.jpg       # jpg files next
#   report.pdf      # pdf files next
#   notes.txt       # txt files last

Come funziona:

  1. La funzione key lambda f: (get_extension(f), f) restituisce una tupla per ciascun nome file
  2. Per "report.pdf", restituisce ("pdf", "report.pdf")
  3. Per "data.csv", restituisce ("csv", "data.csv")
  4. Python ordina in base al primo elemento della tupla (estensione), poi in base al secondo elemento (nome file completo)
  5. Questo raggruppa i file per estensione e ordina alfabeticamente all’interno di ogni gruppo

Perché usare una funzione di supporto?

Confronta la leggibilità:

python
# Senza funzione di supporto - più difficile da capire
sorted_files = sorted(files, key=lambda f: (f.split(".")[-1], f))
 
# Con funzione di supporto - intento più chiaro
sorted_files = sorted(files, key=lambda f: (get_extension(f), f))

La funzione di supporto rende il tuo codice auto-documentante. Chiunque legga get_extension(f) capisce immediatamente cosa sta succedendo, mentre f.split(".")[-1] richiede un’interpretazione mentale.

37.5.8) sorted() vs .sort(): quando usare ciascuno

Python offre due modi per ordinare:

  1. sorted() - Funzione che restituisce una nuova lista ordinata
  2. .sort() - Metodo della lista che ordina in-place
python
# sorted() - crea una nuova lista, l'originale resta invariata
numbers = [3, 1, 4, 1, 5]
sorted_numbers = sorted(numbers)
print(f"Original: {numbers}")        # Output: Original: [3, 1, 4, 1, 5]
print(f"Sorted: {sorted_numbers}")   # Output: Sorted: [1, 1, 3, 4, 5]
 
# .sort() - modifica la lista in-place, restituisce None
numbers = [3, 1, 4, 1, 5]
result = numbers.sort()
print(f"Modified: {numbers}")        # Output: Modified: [1, 1, 3, 4, 5]
print(f"Return value: {result}")     # Output: Return value: None

Quando usare sorted():

  • Devi mantenere l’ordine originale
  • Stai ordinando qualcosa che non è una lista (tupla, stringa, set, ecc.)
  • Vuoi ordinare e assegnare in un’unica espressione

Quando usare .sort():

  • Hai una lista e non ti serve l’ordine originale
  • Vuoi risparmiare memoria (nessuna nuova lista creata)
  • Stai ordinando una lista grande in-place per efficienza

La funzione sorted() è uno degli strumenti più versatili di Python. Combinata con il parametro key, può gestire praticamente qualsiasi esigenza di ordinamento, dal semplice ordinamento numerico al complesso ordinamento multi-criterio di strutture dati annidate.


Questo capitolo ti ha fornito le funzioni integrate e gli strumenti essenziali di Python. Hai imparato a:

  • Comprendere la gerarchia dei tipi di Python e prevedere quali operazioni funzionano con quali tipi
  • Usare funzioni fondamentali come len(), sum(), min(), max(), abs() e round() per operazioni comuni
  • Iterare con informazioni di posizione usando enumerate()
  • Elaborare sequenze parallele contemporaneamente con zip()
  • Prendere decisioni su collezioni usando any() e all()
  • Ordinare i dati in modo flessibile con sorted() e funzioni key personalizzate

Questi strumenti costituiscono la base del codice idiomatico di Python. Sono efficienti, leggibili e gestiscono correttamente i casi limite. Man mano che continui a programmare, ti ritroverai a ricorrere continuamente a queste funzioni—sono i mattoni che rendono il codice Python elegante ed espressivo.

Nel prossimo capitolo esploreremo i decoratori, che ti permettono di modificare e migliorare il comportamento di una funzione(function) in modi potenti, basandoci sui concetti di funzioni di prima classe che abbiamo imparato nel Capitolo 23.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai