Python & AI Tutorials Logo
Programmazione Python

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:

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

python
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):

python
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):

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

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

python
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):

python
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)

Funzioni del modulo random

randint: Interi casuali

random/uniform: Float casuali

choice: Scegli un elemento

sample: Scegli più elementi unici

shuffle: Riordina la lista in place

Inclusivo a entrambi gli estremi

random: da 0.0 a 1.0

uniform: Intervallo personalizzato

Probabilità uguale per ciascuno

Nessun duplicato

Modifica la lista originale

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:

python
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: 45

Per ottenere solo la data (senza l’ora), usa la classe date:

python
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: 2

39.2.2) Creare date e orari specifici

Puoi creare oggetti datetime e date per punti specifici nel tempo:

python
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:00

Questo è utile per rappresentare scadenze, appuntamenti, date storiche o qualsiasi punto fisso nel tempo:

python
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-30

39.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:

python
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): 30

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

python
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-03

timedelta può rappresentare giorni, secondi, microsecondi, millisecondi, minuti, ore e settimane:

python
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.5

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

python
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-26

39.2.4) Confrontare date e orari

Gli oggetti date e datetime possono essere confrontati usando gli operatori di confronto standard:

python
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: True

Questo è utile per verificare scadenze, validare intervalli di date e ordinare date:

python
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-15

39.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:

python
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 PM

Codici di formato comuni:

CodeDescrizioneEsempio
%YAnno con secolo2026
%mMese come numero con zero iniziale (01-12)01
%dGiorno come numero con zero iniziale (01-31)02
%BNome completo del meseJanuary
%bNome abbreviato del meseJan
%ANome completo del giorno della settimanaFriday
%aNome abbreviato del giorno della settimanaFri
%HOra in formato 24 ore (00-23)14
%IOra in formato 12 ore (01-12)02
%MMinuto (00-59)30
%SSecondo (00-59)45
%pAM/PMPM

Ecco un esempio pratico che crea una voce di log:

python
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 successfully

39.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:

python
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:00

Questo è essenziale quando leggi date da file, input utente o fonti di dati esterne:

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

python
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'

Modulo datetime

datetime.now: Data/ora corrente

date.today: Data corrente

datetime/date: Crea date specifiche

timedelta: Durate di tempo

strftime: Formatta in stringa

strptime: Analizza da stringa

Aggiungi/sottrai dalle date

Calcola differenze

%Y, %m, %d, %H, %M, %S

Deve corrispondere esattamente al formato

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 (come None in Python)

Differenze chiave rispetto a Python:

  • JSON usa true/false/null invece di True/False/None di 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:

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:

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

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

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.0

true, false e null di JSON vengono convertiti automaticamente in True, False e None di Python:

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:

python
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.json

Dopo aver eseguito questo codice, il file students.json contiene:

json
[
  {
    "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:

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.9

39.3.6) Gestire gli errori JSON

Quando lavori con JSON, potresti incontrare dati non validi. Gestisci sempre i potenziali errori:

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

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

python
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: 85

Modulo json

dumps: Python → stringa JSON

loads: stringa JSON → Python

dump: Python → file JSON

load: file JSON → Python

parametro indent per leggibilità

Gestisce le conversioni di tipo

Più efficiente di dumps + write

Gestire JSONDecodeError

39.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

python
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

python
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 o None, 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
python
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:

python
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 excluded

Esempio pratico: analizzare i voti degli studenti

python
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 dict normale 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 - restituisce 0 (utile per contare)
  • list - restituisce [] (utile per raggruppare elementi)
  • set - restituisce set() (utile per raccogliere elementi unici)
  • str - restituisce '' (utile per concatenazione di stringhe)
  • lambda: value - restituisce un valore predefinito personalizzato
python
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/A

Uso di base: contare con defaultdict

Confronta un dizionario normale vs defaultdict per il conteggio:

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

  1. Quando accedi a letter_counts[letter] per una nuova lettera, defaultdict chiama int() che restituisce 0
  2. La chiave viene creata con valore 0, poi += 1 la porta a 1
  3. Per le chiavi esistenti, si comporta come un dizionario normale

Raggruppare elementi con defaultdict(list)

Un caso d’uso comune è raggruppare elementi in categorie:

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

  1. Quando accedi a students_by_grade[grade] per un nuovo voto, defaultdict chiama list() che restituisce []
  2. La chiave viene creata con una lista vuota, poi .append(name) aggiunge il primo studente
  3. Per i voti esistenti, fa solo append nella lista esistente

Creare defaultdict da un dizionario esistente

Puoi inizializzare un defaultdict con dati esistenti:

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

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

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

python
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: Iris

Puoi concatenare tipi diversi di iterabili:

python
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, break o zip())
  • Efficiente in memoria: non crea copie dei dati
python
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: red

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

python
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: white

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

python
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()  # Newline

Questo crea un pattern ripetuto: 1, 2, 3, 10, 20, 1, 2, 3, 10, 20, ...

Ecco un esempio pratico che crea una pianificazione a rotazione:

python
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 staging

Modulo itertools

chain: Combina iterabili

cycle: Ripete per sempre

Più efficiente in memoria di +

Funziona con tipi diversi

Crea un iteratore infinito

Usare sempre con una condizione di arresto

Utile per pattern alternati


In 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 test
  • datetime: lavora con date, orari e durate—calcola età, pianifica eventi e formatta timestamp
  • json: scambia dati con altri programmi usando il formato JSON universale—salva lo stato dell’applicazione, lavora con web API e memorizza configurazioni
  • collections: usa contenitori specializzati come Counter per contare e defaultdict per creare automaticamente chiavi
  • itertools: crea iteratori efficienti con chain() per combinare sequenze e cycle() 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.

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