39. Moduli essenziali della libreria standard
La libreria standard di Python è una raccolta di moduli che sono inclusi con Python: non devi installare nulla di aggiuntivo per usarli. Questi moduli forniscono strumenti potenti per attività di programmazione comuni: generare numeri casuali, lavorare con date e orari, scambiare dati con altri programmi e usare strutture dati specializzate che vanno oltre liste e dizionari di base.
In questo capitolo esploreremo cinque moduli essenziali della libreria standard che userai spesso nello sviluppo di applicazioni Python reali.
39.1) Generare casualità con random
Il modulo random fornisce funzioni per generare numeri casuali e fare selezioni casuali. È utile per simulazioni, giochi, test, campionamento casuale di dati e qualsiasi situazione in cui ti serva un comportamento imprevedibile.
39.1.1) Generare interi casuali con randint()
La funzione randint() genera un intero casuale tra due valori, inclusivo a entrambi gli estremi:
import random
# Simula il lancio di un dado a sei facce
die_roll = random.randint(1, 6)
print(f"You rolled: {die_roll}") # Output: You rolled: 4 (varies each run)
# Genera un'età casuale tra 18 e 65
age = random.randint(18, 65)
print(f"Random age: {age}") # Output: Random age: 42 (varies)Nota che sia il valore iniziale sia quello finale sono inclusi nei possibili risultati. randint(1, 6) può restituire 1, 2, 3, 4, 5 o 6: tutti e sei i valori sono possibili.
Ecco un esempio pratico che simula più lanci di dado:
import random
# Simula il lancio di due dadi e calcola la loro somma
die1 = random.randint(1, 6)
die2 = random.randint(1, 6)
total = die1 + die2
print(f"Die 1: {die1}") # Output: Die 1: 3 (varies)
print(f"Die 2: {die2}") # Output: Die 2: 5 (varies)
print(f"Total: {total}") # Output: Total: 8 (varies)
if total == 7:
print("Lucky seven!")
elif total == 2 or total == 12:
print("Snake eyes or boxcars!")Perché entrambi gli estremi sono inclusivi: questo rende randint() intuitiva per casi d’uso comuni. Quando vuoi un numero da 1 a 6 (come un dado), scrivi randint(1, 6) e sia 1 sia 6 sono risultati possibili.
39.1.2) Generare numeri in virgola mobile casuali
Per numeri decimali casuali, usa random() (restituisce un float tra 0.0 e 1.0) oppure uniform() (restituisce un float tra due valori specificati):
import random
# Genera un float casuale tra 0.0 e 1.0 (0.0 incluso, 1.0 escluso)
probability = random.random()
print(f"Random probability: {probability:.4f}") # Output: Random probability: 0.7284 (varies)
# Genera una temperatura casuale tra 15.0 e 30.0 gradi
temperature = random.uniform(15.0, 30.0)
print(f"Temperature: {temperature:.2f}°C") # Output: Temperature: 23.47°C (varies)
# Genera un prezzo casuale tra $10.00 e $99.99
price = random.uniform(10.0, 99.99)
print(f"Price: ${price:.2f}") # Output: Price: $45.67 (varies)La funzione random() è utile quando ti serve un valore di probabilità o una percentuale. La funzione uniform() è migliore quando ti serve un decimale casuale in un intervallo specifico.
39.1.3) Fare scelte casuali con choice()
La funzione choice() seleziona casualmente un elemento da una sequenza (lista, tupla o stringa):
import random
# Seleziona casualmente un colore
colors = ["red", "blue", "green", "yellow", "purple"]
selected_color = random.choice(colors)
print(f"Selected color: {selected_color}") # Output: Selected color: green (varies)
# Seleziona casualmente un vincitore tra i partecipanti
participants = ["Alice", "Bob", "Charlie", "Diana"]
winner = random.choice(participants)
print(f"The winner is: {winner}") # Output: The winner is: Bob (varies)
# Seleziona casualmente un carattere da una stringa
vowels = "aeiou"
random_vowel = random.choice(vowels)
print(f"Random vowel: {random_vowel}") # Output: Random vowel: i (varies)Questo è particolarmente utile per giochi, campionamento casuale o selezione di dati di test casuali. Ogni elemento nella sequenza ha la stessa probabilità di essere scelto.
Ecco un esempio più complesso che simula un semplice gioco a quiz:
import random
# Domande del quiz con le rispettive risposte
questions = [
("What is 2 + 2?", "4"),
("What is the capital of France?", "Paris"),
("What color is the sky?", "blue")
]
# Seleziona casualmente una domanda
question, correct_answer = random.choice(questions)
print(f"Question: {question}")
user_answer = input("Your answer: ")
if user_answer.lower() == correct_answer.lower():
print("Correct!")
else:
print(f"Wrong! The answer was: {correct_answer}")39.1.4) Selezionare più elementi casuali con sample()
Quando devi selezionare più elementi unici da una sequenza, usa sample(). È come pescare carte da un mazzo senza reinserimento: una volta selezionato un elemento, non verrà selezionato di nuovo:
import random
# Seleziona 3 studenti casuali per un progetto di gruppo
students = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
group = random.sample(students, 3)
print(f"Group members: {group}") # Output: Group members: ['Diana', 'Alice', 'Frank'] (varies)
# Estrae 5 numeri della lotteria da 1 a 50 (senza duplicati)
lottery_numbers = random.sample(range(1, 51), 5)
lottery_numbers.sort() # Ordina per la visualizzazione
print(f"Lottery numbers: {lottery_numbers}") # Output: Lottery numbers: [7, 15, 23, 38, 49] (varies)Il secondo argomento di sample() specifica quanti elementi selezionare. Il numero deve essere minore o uguale alla lunghezza della sequenza: non puoi selezionare più elementi di quelli disponibili.
39.1.5) Mescolare sequenze con shuffle()
La funzione shuffle() riordina casualmente gli elementi di una lista in place (modificando la lista originale):
import random
# Mescola un mazzo di carte
cards = ["A♠", "K♠", "Q♠", "J♠", "10♠", "9♠", "8♠", "7♠"]
print(f"Original: {cards}")
random.shuffle(cards)
print(f"Shuffled: {cards}") # Output: Shuffled: ['Q♠', '7♠', 'A♠', '10♠', '9♠', 'J♠', 'K♠', '8♠'] (varies)
# Mescola le domande di un quiz per un ordine casuale
questions = ["Question 1", "Question 2", "Question 3", "Question 4"]
random.shuffle(questions)
print(f"Randomized order: {questions}") # Output: Randomized order: ['Question 3', 'Question 1', 'Question 4', 'Question 2'] (varies)39.2) Lavorare con date e orari
Il modulo datetime fornisce classi per lavorare con date, orari e intervalli di tempo. È essenziale per pianificazione, logging, calcolo di durate e qualsiasi applicazione che debba tracciare quando accadono le cose.
39.2.1) Ottenere la data e l'ora correnti
La classe datetime rappresenta un punto specifico nel tempo con componenti di data e ora:
from datetime import datetime
# Ottieni la data e l'ora correnti
now = datetime.now()
print(f"Current datetime: {now}")
# Output: Current datetime: 2026-01-02 14:30:45.123456
# Accedi ai singoli componenti
print(f"Year: {now.year}") # Output: Year: 2026
print(f"Month: {now.month}") # Output: Month: 1
print(f"Day: {now.day}") # Output: Day: 2
print(f"Hour: {now.hour}") # Output: Hour: 14
print(f"Minute: {now.minute}") # Output: Minute: 30
print(f"Second: {now.second}") # Output: Second: 45Per ottenere solo la data (senza l’ora), usa la classe date:
from datetime import date
# Ottieni la data di oggi
today = date.today()
print(f"Today: {today}") # Output: Today: 2026-01-02
print(f"Year: {today.year}") # Output: Year: 2026
print(f"Month: {today.month}") # Output: Month: 1
print(f"Day: {today.day}") # Output: Day: 239.2.2) Creare date e orari specifici
Puoi creare oggetti datetime e date per punti specifici nel tempo:
from datetime import datetime, date
# Crea una data specifica
birthday = date(1995, 7, 15)
print(f"Birthday: {birthday}") # Output: Birthday: 1995-07-15
# Crea un datetime specifico
meeting = datetime(2026, 3, 15, 14, 30) # March 15, 2026 at 2:30 PM
print(f"Meeting: {meeting}") # Output: Meeting: 2026-03-15 14:30:00Questo è utile per rappresentare scadenze, appuntamenti, date storiche o qualsiasi punto fisso nel tempo:
from datetime import date
# Date importanti in un progetto
project_start = date(2026, 1, 15)
project_end = date(2026, 6, 30)
print(f"Project duration: {project_start} to {project_end}")
# Output: Project duration: 2026-01-15 to 2026-06-3039.2.3) Calcolare differenze di tempo con timedelta
La classe timedelta rappresenta una durata: la differenza tra due date o orari. Puoi usarla per calcolare quanto tempo è passato o per aggiungere/sottrarre tempo alle date:
from datetime import date, timedelta
# Calcola l'età
birth_date = date(1995, 7, 15)
today = date(2026, 1, 2)
age_delta = today - birth_date
print(f"Days since birth: {age_delta.days}") # Output: Days since birth: 11128
print(f"Years (approximate): {age_delta.days // 365}") # Output: Years (approximate): 30Quando sottrai una data da un’altra, ottieni un oggetto timedelta. L’attributo days ti dice il numero di giorni in quella durata.
Puoi anche creare oggetti timedelta direttamente per rappresentare durate specifiche:
from datetime import date, timedelta
# Aggiungi giorni a una data
today = date(2026, 1, 2)
one_week = timedelta(days=7)
next_week = today + one_week
print(f"Today: {today}") # Output: Today: 2026-01-02
print(f"Next week: {next_week}") # Output: Next week: 2026-01-09
# Sottrai giorni da una data
thirty_days_ago = today - timedelta(days=30)
print(f"30 days ago: {thirty_days_ago}") # Output: 30 days ago: 2025-12-03timedelta può rappresentare giorni, secondi, microsecondi, millisecondi, minuti, ore e settimane:
from datetime import datetime, timedelta
# Calcola una scadenza
now = datetime(2026, 1, 2, 14, 30)
deadline = now + timedelta(hours=48, minutes=30)
print(f"Current time: {now}") # Output: Current time: 2026-01-02 14:30:00
print(f"Deadline: {deadline}") # Output: Deadline: 2026-01-04 15:00:00
# Calcola il tempo rimanente
time_left = deadline - now
print(f"Hours remaining: {time_left.total_seconds() / 3600}") # Output: Hours remaining: 48.5Il metodo total_seconds() converte l’intera durata in secondi, che poi puoi convertire in ore, minuti o qualsiasi altra unità.
Ecco un esempio pratico che calcola le milestone di progetto:
from datetime import date, timedelta
# Pianificazione del progetto
project_start = date(2026, 1, 15)
sprint_duration = timedelta(weeks=2)
sprint_1_end = project_start + sprint_duration
sprint_2_end = sprint_1_end + sprint_duration
sprint_3_end = sprint_2_end + sprint_duration
print(f"Sprint 1: {project_start} to {sprint_1_end}")
# Output: Sprint 1: 2026-01-15 to 2026-01-29
print(f"Sprint 2: {sprint_1_end} to {sprint_2_end}")
# Output: Sprint 2: 2026-01-29 to 2026-02-12
print(f"Sprint 3: {sprint_2_end} to {sprint_3_end}")
# Output: Sprint 3: 2026-02-12 to 2026-02-2639.2.4) Confrontare date e orari
Gli oggetti date e datetime possono essere confrontati usando gli operatori di confronto standard:
from datetime import date
# Confronta le date
date1 = date(2026, 1, 15)
date2 = date(2026, 2, 20)
date3 = date(2026, 1, 15)
print(date1 < date2) # Output: True
print(date1 == date3) # Output: True
print(date2 > date1) # Output: TrueQuesto è utile per verificare scadenze, validare intervalli di date e ordinare date:
from datetime import date
# Controlla se una data è nel passato
event_date = date(2025, 12, 25)
today = date(2026, 1, 2)
if event_date < today:
print("This event has already passed") # Output: This event has already passed
else:
print("This event is upcoming")
# Ordina una lista di date
important_dates = [
date(2026, 3, 15),
date(2026, 1, 10),
date(2026, 2, 28)
]
important_dates.sort()
print("Dates in order:") # Output: Dates in order:
for d in important_dates:
print(f" {d}")
# Output:
# 2026-01-10
# 2026-02-28
# 2026-03-1539.2.5) Formattare date e orari con strftime()
Il metodo strftime() (string format time) converte date e orari in stringhe formattate. Specifichi il formato usando codici speciali:
from datetime import datetime
now = datetime(2026, 1, 2, 14, 30, 45)
# Formati data comuni
print(now.strftime("%Y-%m-%d")) # Output: 2026-01-02
print(now.strftime("%m/%d/%Y")) # Output: 01/02/2026
print(now.strftime("%B %d, %Y")) # Output: January 02, 2026
print(now.strftime("%A, %B %d, %Y")) # Output: Friday, January 02, 2026
# Formati ora comuni
print(now.strftime("%H:%M:%S")) # Output: 14:30:45
print(now.strftime("%I:%M %p")) # Output: 02:30 PM
# Formati combinati
print(now.strftime("%Y-%m-%d %H:%M:%S")) # Output: 2026-01-02 14:30:45
print(now.strftime("%B %d, %Y at %I:%M %p")) # Output: January 02, 2026 at 02:30 PMCodici di formato comuni:
| Code | Descrizione | Esempio |
|---|---|---|
%Y | Anno con secolo | 2026 |
%m | Mese come numero con zero iniziale (01-12) | 01 |
%d | Giorno come numero con zero iniziale (01-31) | 02 |
%B | Nome completo del mese | January |
%b | Nome abbreviato del mese | Jan |
%A | Nome completo del giorno della settimana | Friday |
%a | Nome abbreviato del giorno della settimana | Fri |
%H | Ora in formato 24 ore (00-23) | 14 |
%I | Ora in formato 12 ore (01-12) | 02 |
%M | Minuto (00-59) | 30 |
%S | Secondo (00-59) | 45 |
%p | AM/PM | PM |
Ecco un esempio pratico che crea una voce di log:
from datetime import datetime
def log_event(message):
"""Registra un evento con un timestamp"""
now = datetime.now()
timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
print(f"[{timestamp}] {message}")
log_event("User logged in")
# Output: [2026-01-02 14:30:45] User logged in
log_event("File uploaded successfully")
# Output: [2026-01-02 14:30:45] File uploaded successfully39.2.6) Analizzare date da stringhe con strptime()
La funzione strptime() (string parse time) converte stringhe formattate di nuovo in oggetti datetime. Specifichi gli stessi codici di formato per dire a Python come interpretare la stringa:
from datetime import datetime
# Analizza diversi formati di data
date_str1 = "2026-01-15"
date1 = datetime.strptime(date_str1, "%Y-%m-%d")
print(f"Parsed: {date1}") # Output: Parsed: 2026-01-15 00:00:00
date_str2 = "January 15, 2026"
date2 = datetime.strptime(date_str2, "%B %d, %Y")
print(f"Parsed: {date2}") # Output: Parsed: 2026-01-15 00:00:00
# Analizza un datetime con l'orario
datetime_str = "2026-01-15 14:30:00"
dt = datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S")
print(f"Parsed: {dt}") # Output: Parsed: 2026-01-15 14:30:00Questo è essenziale quando leggi date da file, input utente o fonti di dati esterne:
from datetime import datetime
# Analizza l'input utente
user_input = "03/15/2026"
try:
event_date = datetime.strptime(user_input, "%m/%d/%Y")
print(f"Event scheduled for: {event_date.strftime('%B %d, %Y')}")
# Output: Event scheduled for: March 15, 2026
except ValueError:
print("Invalid date format. Please use MM/DD/YYYY")Importante: la stringa di formato deve corrispondere esattamente alla stringa di input, altrimenti otterrai un ValueError:
from datetime import datetime
# Questo fallirà - il formato non corrisponde
try:
datetime.strptime("2026-01-15", "%m/%d/%Y") # Wrong format
except ValueError as e:
print(f"Error: {e}")
# Output: Error: time data '2026-01-15' does not match format '%m/%d/%Y'39.3) Leggere e scrivere dati JSON
JSON (JavaScript Object Notation) è un formato testuale per memorizzare e scambiare dati strutturati. È il formato più comune per API web, file di configurazione e scambio di dati tra programmi. Il modulo json di Python rende facile convertire tra strutture dati Python e testo JSON.
39.3.1) Comprendere la struttura di JSON
JSON assomiglia ai dizionari e alle liste di Python, ma con alcune differenze:
JSON supporta questi tipi di dati:
- Oggetti (come i dizionari Python):
{"name": "Alice", "age": 30} - Array (come le liste Python):
[1, 2, 3, 4] - Stringhe:
"hello"(devono usare doppie virgolette) - Numeri:
42,3.14 - Booleani:
true,false(minuscoli) - Null:
null(comeNonein Python)
Differenze chiave rispetto a Python:
- JSON usa
true/false/nullinvece diTrue/False/Nonedi Python - Le stringhe JSON devono usare doppie virgolette (
"text"), non virgolette singole - JSON non supporta direttamente tuple, set o oggetti personalizzati
Ecco com’è fatto un dato JSON:
{
"name": "Alice Johnson",
"age": 30,
"email": "alice@example.com",
"is_active": true,
"scores": [85, 92, 78, 95],
"address": {
"street": "123 Main St",
"city": "Springfield",
"zip": "12345"
}
}Nota: questo è puro testo JSON, non codice Python. Nota il true in minuscolo e l’uso delle doppie virgolette.
39.3.2) Convertire dati Python in JSON con dumps()
La funzione dumps() (dump string) converte strutture dati Python in stringhe formattate come JSON:
import json
student = {
"name": "Alice Johnson",
"age": 30,
"email": "alice@example.com",
"is_active": True,
"scores": [85, 92, 78, 95]
}
# Converte un dizionario in JSON
json_string = json.dumps(student)
print(json_string)
# Output: {"name": "Alice Johnson", "age": 30, "email": "alice@example.com", "is_active": true, "scores": [85, 92, 78, 95]}
print(type(json_string)) # Output: <class 'str'>Nota come True di Python sia diventato true di JSON nell’output. La funzione dumps() gestisce automaticamente queste conversioni.
Per un output più leggibile, usa il parametro indent:
import json
student = {
"name": "Alice Johnson",
"age": 30,
"scores": [85, 92, 78, 95]
}
# Stampa “pretty” con indentazione
json_string = json.dumps(student, indent=2)
print(json_string)
# Output:
# {
# "name": "Alice Johnson",
# "age": 30,
# "scores": [
# 85,
# 92,
# 78,
# 95
# ]
# }Il parametro indent specifica quanti spazi usare per ogni livello di indentazione. Questo rende il JSON molto più facile da leggere, specialmente per strutture annidate complesse.
39.3.3) Convertire JSON in dati Python con loads()
La funzione loads() (load string) converte stringhe formattate come JSON di nuovo in strutture dati Python:
import json
# Stringa JSON (come potresti riceverla da una web API)
json_string = '{"name": "Bob Smith", "age": 25, "scores": [90, 88, 92]}'
# Converte in dizionario Python
student = json.loads(json_string)
print(student) # Output: {'name': 'Bob Smith', 'age': 25, 'scores': [90, 88, 92]}
print(type(student)) # Output: <class 'dict'>
# Accedi ai dati come a qualsiasi dizionario Python
print(f"Name: {student['name']}") # Output: Name: Bob Smith
print(f"Average score: {sum(student['scores']) / len(student['scores'])}")
# Output: Average score: 90.0true, false e null di JSON vengono convertiti automaticamente in True, False e None di Python:
import json
json_string = '{"active": true, "verified": false, "middle_name": null}'
data = json.loads(json_string)
print(data) # Output: {'active': True, 'verified': False, 'middle_name': None}
print(type(data["active"])) # Output: <class 'bool'>
print(type(data["middle_name"])) # Output: <class 'NoneType'>39.3.4) Scrivere JSON su file con dump()
La funzione dump() scrive i dati Python direttamente su un file in formato JSON:
import json
# Archivi degli studenti
students = [
{"name": "Alice", "age": 20, "gpa": 3.8},
{"name": "Bob", "age": 22, "gpa": 3.5},
{"name": "Charlie", "age": 21, "gpa": 3.9}
]
# Scrive su un file JSON
with open("students.json", "w") as file:
json.dump(students, file, indent=2)
print("Data written to students.json")
# Output: Data written to students.jsonDopo aver eseguito questo codice, il file students.json contiene:
[
{
"name": "Alice",
"age": 20,
"gpa": 3.8
},
{
"name": "Bob",
"age": 22,
"gpa": 3.5
},
{
"name": "Charlie",
"age": 21,
"gpa": 3.9
}
]Perché usare dump() invece di dumps()? La funzione dump() scrive direttamente su un file, il che è più efficiente che convertire prima in una stringa e poi scrivere la stringa. Usa dump() per i file e dumps() quando ti serve il JSON come stringa (per esempio, da inviare su una rete).
39.3.5) Leggere JSON da file con load()
La funzione load() legge dati JSON da un file e li converte in strutture dati Python:
import json
# Leggi dal file JSON creato in precedenza
with open("students.json", "r") as file:
students = json.load(file)
print(f"Loaded {len(students)} students") # Output: Loaded 3 students
# Lavora con i dati
for student in students:
print(f"{student['name']}: GPA {student['gpa']}")
# Output:
# Alice: GPA 3.8
# Bob: GPA 3.5
# Charlie: GPA 3.939.3.6) Gestire gli errori JSON
Quando lavori con JSON, potresti incontrare dati non validi. Gestisci sempre i potenziali errori:
import json
# JSON non valido - manca la virgoletta di chiusura
invalid_json = '{"name": "Alice", "age": 30'
try:
data = json.loads(invalid_json)
except json.JSONDecodeError as e:
print(f"Invalid JSON: {e}")
# Output: Invalid JSON: Expecting ',' delimiter: line 1 column 28 (char 27)Questo è particolarmente importante quando leggi JSON da fonti esterne (file, web API, input utente) dove non puoi garantire che i dati siano validi:
import json
def load_config(filename):
"""Carica la configurazione da un file JSON con gestione degli errori"""
try:
with open(filename, "r") as file:
config = json.load(file)
return config
except FileNotFoundError:
print(f"Config file '{filename}' not found")
return None
except json.JSONDecodeError as e:
print(f"Invalid JSON in '{filename}': {e}")
return None
# Prova a caricare la configurazione
config = load_config("config.json")
if config:
print(f"Configuration loaded: {config}")
else:
print("Using default configuration")39.3.7) Esempio pratico JSON: salvare e caricare lo stato dell'applicazione
Ecco un esempio completo che mostra come salvare e caricare i dati dell’applicazione:
import json
def save_game_state(filename, player_data):
"""Salva lo stato del gioco in un file JSON"""
with open(filename, "w") as file:
json.dump(player_data, file, indent=2)
print(f"Game saved to {filename}")
def load_game_state(filename):
"""Carica lo stato del gioco da un file JSON"""
try:
with open(filename, "r") as file:
player_data = json.load(file)
print(f"Game loaded from {filename}")
return player_data
except FileNotFoundError:
print("No saved game found")
return None
# Dati del gioco
player = {
"name": "Hero",
"level": 5,
"health": 85,
"inventory": ["sword", "shield", "potion"],
"position": {"x": 10, "y": 20}
}
# Salva il gioco
save_game_state("savegame.json", player)
# Output: Game saved to savegame.json
# Più tardi, carica il gioco
loaded_player = load_game_state("savegame.json")
# Output: Game loaded from savegame.json
if loaded_player:
print(f"Welcome back, {loaded_player['name']}!")
print(f"Level: {loaded_player['level']}, Health: {loaded_player['health']}")
# Output:
# Welcome back, Hero!
# Level: 5, Health: 8539.4) Contenitori pratici in collections
Il modulo collections fornisce tipi di contenitori specializzati che estendono i contenitori integrati di Python (liste, dizionari, set) con funzionalità aggiuntive. Questi contenitori risolvono problemi comuni in modo più elegante rispetto all’uso di strutture dati di base.
39.4.1) Contare elementi con Counter
La classe Counter è progettata per contare oggetti hashabili. È una sottoclasse di dizionario che memorizza gli elementi come chiavi e i loro conteggi come valori.
Cosa accetta Counter come input:
- Qualsiasi iterabile (lista, stringa, tupla, ecc.)
- Un altro dizionario con i conteggi
- Argomenti keyword con i conteggi
Cosa memorizza Counter:
- Un dizionario in cui le chiavi sono gli elementi e i valori sono i loro conteggi
- Esempio:
Counter(['a', 'b', 'a'])memorizza{'a': 2, 'b': 1}
Vantaggio chiave rispetto ai dizionari normali:
- Restituisce 0 per le chiavi mancanti invece di sollevare
KeyError - Fornisce metodi specifici per i conteggi come
most_common() - Supporta operazioni aritmetiche tra counter
Uso di base
from collections import Counter
# Conta le lettere in una parola
word = "mississippi"
letter_counts = Counter(word)
print(letter_counts)
# Output: Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})
# Accedi ai conteggi come a un dizionario
print(f"Number of 'i's: {letter_counts['i']}")
# Output: Number of 'i's: 4
print(f"Number of 'z's: {letter_counts['z']}")
# Output: Number of 'z's: 0 (returns 0 for missing keys, no KeyError!)Creare Counter da fonti diverse
from collections import Counter
# Da una lista
votes = ["Alice", "Bob", "Alice", "Charlie", "Alice", "Bob", "Alice"]
vote_counts = Counter(votes)
print(vote_counts)
# Output: Counter({'Alice': 4, 'Bob': 2, 'Charlie': 1})
# Da una stringa (conta ciascun carattere)
letter_counts = Counter("hello")
print(letter_counts)
# Output: Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1})
# Da un dizionario
existing_counts = {'apple': 3, 'banana': 2}
fruit_counts = Counter(existing_counts)
print(fruit_counts)
# Output: Counter({'apple': 3, 'banana': 2})
# Da argomenti keyword
color_counts = Counter(red=5, blue=3, green=2)
print(color_counts)
# Output: Counter({'red': 5, 'blue': 3, 'green': 2})Trovare gli elementi più comuni con most_common()
Firma del metodo: most_common(n=None)
Parametri:
n(opzionale): numero di elementi più comuni da restituire- Se
nè omesso oNone, restituisce tutti gli elementi
Restituisce:
- Una lista di tuple
(element, conteggio) - Ordinata per conteggio, dal più alto al più basso
- Se i conteggi sono uguali, gli elementi sono nell’ordine in cui sono stati incontrati per primi
from collections import Counter
# Analizza la frequenza delle parole nel testo
text = "the quick brown fox jumps over the lazy dog the fox"
words = text.split()
word_counts = Counter(words)
# Ottieni le 3 parole più comuni
top_3 = word_counts.most_common(3)
print(top_3)
# Output: [('the', 3), ('fox', 2), ('quick', 1)]Operazioni aritmetiche sui Counter
Puoi sommare, sottrarre e fare altre operazioni su oggetti Counter:
from collections import Counter
# Conta gli elementi in due gruppi
group1 = Counter(["apple", "banana", "apple", "orange"])
print(group1)
# Output: Counter({'apple': 2, 'banana': 1, 'orange': 1})
group2 = Counter(["banana", "banana", "grape", "apple"])
print(group2)
# Output: Counter({'banana': 2, 'grape': 1, 'apple': 1})
# Somma i conteggi
combined = group1 + group2
print(combined)
# Output: Counter({'apple': 3, 'banana': 3, 'orange': 1, 'grape': 1})
# Sottrai i conteggi (mantiene solo risultati positivi)
difference = group1 - group2
print(difference)
# Output: Counter({'apple': 1, 'orange': 1})
# banana: 1 - 2 = -1 (negative, so excluded)
# grape: not in group1, so excludedEsempio pratico: analizzare i voti degli studenti
from collections import Counter
# Distribuzione dei voti
grades = ["A", "B", "A", "C", "B", "A", "B", "D", "A", "B", "C", "A"]
grade_counts = Counter(grades)
print(f"Total students: {len(grades)}")
# Output: Total students: 12
print("\nGrade Distribution:")
for grade, count in grade_counts.most_common():
percentage = (count / len(grades)) * 100
bar = "█" * count
print(f" {grade}: {count} students ({percentage:4.1f}%) {bar}")
# Output:
# Grade Distribution:
# A: 5 students (41.7%) █████
# B: 4 students (33.3%) ████
# C: 2 students (16.7%) ██
# D: 1 students ( 8.3%) █39.4.2) Dizionari con valori predefiniti usando defaultdict
La classe defaultdict è una sottoclasse di dizionario che crea automaticamente voci con un valore predefinito quando accedi a una chiave mancante. Questo elimina la necessità di controllare se le chiavi esistono prima di usarle.
Cosa accetta defaultdict come input:
- Una funzione default factory (obbligatoria): un callable che restituisce il valore predefinito per le chiavi mancanti
- Qualsiasi argomento che un
dictnormale accetta (coppie chiave-valore, un altro dizionario, argomenti keyword)
Vantaggio chiave rispetto ai dizionari normali:
- Non serve controllare se una chiave esiste prima di usarla
- Inizializza automaticamente le chiavi mancanti con un valore predefinito
- Codice più pulito e leggibile per operazioni di raggruppamento, conteggio e accumulo
Comprendere la Default Factory
Quando crei un defaultdict, devi fornire una default factory: un callable (funzione) che non accetta argomenti e restituisce il valore predefinito. Default factory comuni:
int- restituisce0(utile per contare)list- restituisce[](utile per raggruppare elementi)set- restituisceset()(utile per raccogliere elementi unici)str- restituisce''(utile per concatenazione di stringhe)lambda: value- restituisce un valore predefinito personalizzato
from collections import defaultdict
# Diverse default factory
counts = defaultdict(int) # Le chiavi mancanti restituiscono 0
groups = defaultdict(list) # Le chiavi mancanti restituiscono []
unique = defaultdict(set) # Le chiavi mancanti restituiscono set()
custom = defaultdict(lambda: "N/A") # Le chiavi mancanti restituiscono "N/A"
# Test con chiavi mancanti
print(counts['missing']) # Output: 0
print(groups['missing']) # Output: []
print(unique['missing']) # Output: set()
print(custom['missing']) # Output: N/AUso di base: contare con defaultdict
Confronta un dizionario normale vs defaultdict per il conteggio:
from collections import defaultdict
word = "mississippi"
# Dizionario normale - serve controllare se la chiave esiste
regular_dict = {}
for letter in word:
if letter not in regular_dict:
regular_dict[letter] = 0
regular_dict[letter] += 1
print(regular_dict)
# Output: {'m': 1, 'i': 4, 's': 4, 'p': 2}
# defaultdict - crea automaticamente voci con valore predefinito
letter_counts = defaultdict(int) # int() restituisce 0
for letter in word:
letter_counts[letter] += 1 # Non serve controllare se la chiave esiste!
print(dict(letter_counts))
# Output: {'m': 1, 'i': 4, 's': 4, 'p': 2}Come funziona:
- Quando accedi a
letter_counts[letter]per una nuova lettera,defaultdictchiamaint()che restituisce0 - La chiave viene creata con valore
0, poi+= 1la porta a1 - Per le chiavi esistenti, si comporta come un dizionario normale
Raggruppare elementi con defaultdict(list)
Un caso d’uso comune è raggruppare elementi in categorie:
from collections import defaultdict
students = [
("Alice", "A"),
("Bob", "B"),
("Charlie", "A"),
("Diana", "C"),
("Eve", "B"),
("Frank", "A")
]
# Raggruppa gli studenti per voto
# Con defaultdict - pulito e semplice
students_by_grade = defaultdict(list)
for name, grade in students:
students_by_grade[grade].append(name)
print(dict(students_by_grade))
# Output: {'A': ['Alice', 'Charlie', 'Frank'], 'B': ['Bob', 'Eve'], 'C': ['Diana']}
# Accedi a un voto che non esiste ancora
print(students_by_grade["D"]) # Output: [] (empty list, not KeyError!)Come funziona:
- Quando accedi a
students_by_grade[grade]per un nuovo voto,defaultdictchiamalist()che restituisce[] - La chiave viene creata con una lista vuota, poi
.append(name)aggiunge il primo studente - Per i voti esistenti, fa solo append nella lista esistente
Creare defaultdict da un dizionario esistente
Puoi inizializzare un defaultdict con dati esistenti:
from collections import defaultdict
# Parti da conteggi esistenti
existing_data = {'apple': 5, 'banana': 3}
# Crea defaultdict da un dizionario esistente
fruit_counts = defaultdict(int, existing_data)
# Aggiungi altri conteggi
fruit_counts['apple'] += 2 # 5 + 2 = 7
fruit_counts['orange'] += 1 # 0 + 1 = 1 (new key, starts at 0)
print(dict(fruit_counts))
# Output: {'apple': 7, 'banana': 3, 'orange': 1}Default factory personalizzata
Puoi fornire qualsiasi callable come default factory:
from collections import defaultdict
# Usa lambda per valori predefiniti personalizzati
page_views = defaultdict(lambda: {'views': 0, 'unique': 0})
page_views['home']['views'] = 100
page_views['home']['unique'] = 75
print(page_views['home'])
# Output: {'views': 100, 'unique': 75}
print(page_views['about']) # New key gets default dictionary
# Output: {'views': 0, 'unique': 0}Note importanti
Accedere vs. controllare le chiavi:
from collections import defaultdict
counts = defaultdict(int)
# Accedere a una chiave mancante la CREA
value = counts['missing'] # Crea 'missing' con valore 0
print('missing' in counts) # Output: True
# Per controllare senza creare, usa 'in' o .get()
counts2 = defaultdict(int)
print('missing' in counts2) # Output: False (doesn't create key)
print(counts2.get('missing')) # Output: None (doesn't create key)39.5) (Opzionale) Strumenti utili per l'iterazione
Il modulo itertools fornisce funzioni per creare iteratori efficienti. Questi strumenti ti aiutano a lavorare con sequenze in modi potenti senza creare grandi liste intermedie.
39.5.1) Concatenare iterabili con chain()
La funzione chain() combina più iterabili in un singolo iteratore che produce gli elementi di ciascun iterabile in sequenza.
Cosa accetta chain():
- Più iterabili (liste, tuple, stringhe, ecc.) come argomenti separati
Cosa restituisce chain():
- Un iteratore che produce tutti gli elementi del primo iterabile, poi tutti quelli del secondo, e così via
Vantaggio chiave:
- Più efficiente in memoria rispetto alla concatenazione con
+(non crea liste intermedie) - Funziona con qualsiasi iterabile, non solo con le liste
from itertools import chain
# Combina più liste
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]
combined = chain(list1, list2, list3)
print(list(combined)) # Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]Questo è più efficiente in memoria rispetto alla concatenazione delle liste con +, specialmente per sequenze grandi:
from itertools import chain
# Elabora più sorgenti di dati senza creare una grande lista combinata
students_class_a = ["Alice", "Bob", "Charlie"]
students_class_b = ["Diana", "Eve", "Frank"]
students_class_c = ["Grace", "Henry", "Iris"]
# Itera su tutti gli studenti senza creare una lista combinata
for student in chain(students_class_a, students_class_b, students_class_c):
print(f"Processing: {student}")
# Output:
# Processing: Alice
# Processing: Bob
# Processing: Charlie
# Processing: Diana
# Processing: Eve
# Processing: Frank
# Processing: Grace
# Processing: Henry
# Processing: IrisPuoi concatenare tipi diversi di iterabili:
from itertools import chain
# Concatena liste, tuple e stringhe
numbers = [1, 2, 3]
letters = ("a", "b", "c")
word = "xyz"
combined = chain(numbers, letters, word)
print(list(combined)) # Output: [1, 2, 3, 'a', 'b', 'c', 'x', 'y', 'z']39.5.2) Ripetere elementi con cycle()
La funzione cycle() crea un iteratore infinito che cicla ripetutamente attraverso gli elementi di un iterabile.
Cosa accetta cycle():
- Un singolo iterabile (lista, tupla, stringa, ecc.)
Cosa restituisce cycle():
- Un iteratore infinito che produce ripetutamente elementi dell’iterabile
- Dopo aver raggiunto la fine, riparte dall’inizio
Caratteristiche chiave:
- Crea un iteratore infinito: non si ferma da solo
- Deve essere usato con una condizione di arresto (contatore,
breakozip()) - Efficiente in memoria: non crea copie dei dati
from itertools import cycle
# Crea un ciclo infinito di colori
colors = cycle(["red", "green", "blue"])
# Prendi i primi 10 colori
for i, color in enumerate(colors):
if i >= 10:
break
print(f"Item {i}: {color}")
# Output:
# Item 0: red
# Item 1: green
# Item 2: blue
# Item 3: red
# Item 4: green
# Item 5: blue
# Item 6: red
# Item 7: green
# Item 8: blue
# Item 9: redAvvertenza: cycle() crea un iteratore infinito. Usalo sempre con una condizione di arresto (come un contatore o un’istruzione break), altrimenti creerai un ciclo infinito.
Un caso d’uso pratico è alternare tra valori:
from itertools import cycle
# Alterna tra due colori di sfondo per le righe di una tabella
row_colors = cycle(["white", "lightgray"])
rows = ["Row 1", "Row 2", "Row 3", "Row 4", "Row 5"]
for row, color in zip(rows, row_colors):
print(f"{row}: background-color: {color}")
# Output:
# Row 1: background-color: white
# Row 2: background-color: lightgray
# Row 3: background-color: white
# Row 4: background-color: lightgray
# Row 5: background-color: whiteQui usiamo zip() (che abbiamo imparato nel Capitolo 37) per accoppiare ogni riga con un colore. L’iteratore cycle() ripete automaticamente i colori secondo necessità.
39.5.3) Combinare chain() e cycle()
Puoi combinare funzioni di itertools per pattern più complessi:
from itertools import chain, cycle
# Crea un pattern che cicla attraverso più sequenze
pattern1 = [1, 2, 3]
pattern2 = [10, 20]
# Concatena i pattern, poi cicla il risultato
combined_pattern = cycle(chain(pattern1, pattern2))
# Prendi i primi 12 valori
for i, value in enumerate(combined_pattern):
if i >= 12:
break
print(value, end=" ")
# Output: 1 2 3 10 20 1 2 3 10 20 1 2
print() # NewlineQuesto crea un pattern ripetuto: 1, 2, 3, 10, 20, 1, 2, 3, 10, 20, ...
Ecco un esempio pratico che crea una pianificazione a rotazione:
from itertools import cycle
# Crea una pianificazione a rotazione per i membri del team
team_members = ["Alice", "Bob", "Charlie"]
schedule = cycle(team_members)
# Assegna i task ai membri del team a rotazione
tasks = [
"Review code",
"Write tests",
"Update documentation",
"Fix bug #123",
"Implement feature X",
"Deploy to staging"
]
print("Task Assignments:")
for task, assignee in zip(tasks, schedule):
print(f" {assignee}: {task}")
# Output:
# Task Assignments:
# Alice: Review code
# Bob: Write tests
# Charlie: Update documentation
# Alice: Fix bug #123
# Bob: Implement feature X
# Charlie: Deploy to stagingIn questo capitolo abbiamo esplorato cinque moduli essenziali della libreria standard che estendono le capacità di Python:
random: genera numeri casuali, effettua selezioni casuali e mescola sequenze—essenziale per simulazioni, giochi e testdatetime: lavora con date, orari e durate—calcola età, pianifica eventi e formatta timestampjson: scambia dati con altri programmi usando il formato JSON universale—salva lo stato dell’applicazione, lavora con web API e memorizza configurazionicollections: usa contenitori specializzati comeCounterper contare edefaultdictper creare automaticamente chiaviitertools: crea iteratori efficienti conchain()per combinare sequenze ecycle()per ripetere pattern
Questi moduli fanno parte della libreria standard di Python: sono sempre disponibili, ben testati e risolvono elegantemente problemi di programmazione comuni. Man mano che costruirai programmi più complessi, ti ritroverai a usare spesso questi strumenti. Rappresentano la filosofia di Python del “batteries included”: fornire soluzioni potenti e pronte all’uso per le attività di programmazione quotidiane.