In questa pagina trovi 10 esercizi di Python ordinati per difficoltà: si parte da print e input e si arriva a dizionari, file JSON e programmazione a oggetti.
Ogni esercizio contiene la consegna, un richiamo di teoria, la soluzione e la spiegazione dettagliata. Per eseguire il codice ti basta Python 3 installato (python3 esercizio.py) oppure un ambiente online.
Indice degli esercizi
- Esercizio 1 – Il primo programma: print e input (Base)
- Esercizio 2 – Calcolatore dell’età in giorni (Base)
- Esercizio 3 – Dal voto al giudizio (Base)
- Esercizio 4 – Somma dei numeri e tabellina con for (Base)
- Esercizio 5 – Indovina il numero (Intermedio)
- Esercizio 6 – Statistiche su una lista (Intermedio)
- Esercizio 7 – Numeri primi con le funzioni (Intermedio)
- Esercizio 8 – Contare le parole di un testo (Avanzato)
- Esercizio 9 – Rubrica salvata su file (Avanzato)
- Esercizio 10 – Conto bancario con le classi (Avanzato)
Esercizio 1 – Il primo programma: print e input
Livello: Base · Argomenti: print, input, stringhe
Consegna: Chiedi all’utente il suo nome e salutalo con un messaggio personalizzato, ad esempio «Ciao Marco, benvenuto in Python!».
Teoria in breve: print() mostra un testo a schermo, input() legge ciò che l’utente scrive e lo restituisce sempre come stringa. In Python non serve dichiarare il tipo delle variabili: basta assegnare un valore.
Soluzione
nome = input("Come ti chiami? ")
print(f"Ciao {nome}, benvenuto in Python!")
Spiegazione
La variabile nome riceve il testo digitato dall’utente.
La f davanti alle virgolette crea una f-string: tutto ciò che è tra parentesi graffe { } viene sostituito con il valore della variabile. È il modo più moderno e leggibile di comporre testi in Python.
Esercizio 2 – Calcolatore dell’età in giorni
Livello: Base · Argomenti: int(), operazioni aritmetiche, conversione di tipo
Consegna: Chiedi l’età in anni e stampa circa quanti mesi, giorni e ore hai vissuto.
Teoria in breve: Poiché input() restituisce una stringa, per fare calcoli bisogna convertirla in numero con int() (interi) o float() (decimali). Gli operatori aritmetici sono + - * /, più // (divisione intera), % (resto) e ** (potenza).
Soluzione
anni = int(input("Quanti anni hai? "))
mesi = anni * 12
giorni = anni * 365
ore = giorni * 24
print(f"Hai vissuto circa {mesi} mesi,")
print(f"{giorni:,} giorni e {ore:,} ore!")
Spiegazione
int(input(...)) legge il testo e lo trasforma subito in numero intero. Se l’utente scrivesse una parola, Python solleverebbe un errore ValueError (lo gestiremo nell’esercizio 10).
Nella f-string {giorni:,} il formato :, aggiunge il separatore delle migliaia, rendendo i numeri grandi più leggibili.
Esercizio 3 – Dal voto al giudizio
Livello: Base · Argomenti: if / elif / else, operatori di confronto
Consegna: Leggi un voto da 1 a 10 e stampa il giudizio: insufficiente (< 6), sufficiente (6), buono (7–8), ottimo (9–10). Se il voto non è valido, segnalalo.
Teoria in breve: Le condizioni si scrivono con if, elif (else if) ed else. In Python i blocchi sono delimitati dall’indentazione (4 spazi), non da parentesi graffe. Python permette anche confronti concatenati come 7 <= voto <= 8.
Soluzione
voto = int(input("Inserisci il voto (1-10): "))
if voto < 1 or voto > 10:
print("Voto non valido")
elif voto < 6:
print("Insufficiente")
elif voto == 6:
print("Sufficiente")
elif 7 <= voto <= 8:
print("Buono")
else:
print("Ottimo")
Spiegazione
Prima controlliamo che il voto sia valido, così gli altri rami lavorano solo su valori corretti. or è l’operatore logico «oppure».
Le condizioni sono valutate dall’alto verso il basso e viene eseguito solo il primo ramo vero. Per questo all’else finale arrivano solo i voti 9 e 10.
Esercizio 4 – Somma dei numeri e tabellina con for
Livello: Base · Argomenti: ciclo for, range()
Consegna: Leggi un numero N, stampa la somma di tutti i numeri da 1 a N e poi la tabellina di N.
Teoria in breve: Il ciclo for in Python scorre gli elementi di una sequenza. range(a, b) genera i numeri da a a b-1: l’estremo finale è escluso.
Soluzione
n = int(input("Inserisci N: "))
somma = 0
for i in range(1, n + 1):
somma += i
print(f"La somma da 1 a {n} è {somma}")
print(f"Tabellina del {n}:")
for i in range(1, 11):
print(f"{n} x {i} = {n * i}")
Spiegazione
Usiamo range(1, n + 1) perché vogliamo includere anche N. somma += i è l’abbreviazione di somma = somma + i: è il classico schema dell’accumulatore.
Curiosità: la stessa somma si ottiene con sum(range(1, n + 1)) oppure con la formula di Gauss n * (n + 1) // 2.
Esercizio 5 – Indovina il numero
Livello: Intermedio · Argomenti: ciclo while, modulo random, break
Consegna: Il programma sceglie un numero casuale tra 1 e 100 e l’utente deve indovinarlo con l’aiuto dei suggerimenti «troppo alto / troppo basso». Alla fine mostra i tentativi usati.
Teoria in breve: Il ciclo while ripete un blocco finché una condizione è vera; con while True si crea un ciclo infinito da cui si esce con break. I moduli estendono Python: random serve a generare numeri casuali.
Soluzione
import random
segreto = random.randint(1, 100)
tentativi = 0
print("Ho pensato un numero tra 1 e 100!")
while True:
tentativo = int(input("Il tuo tentativo: "))
tentativi += 1
if tentativo < segreto:
print("Troppo basso!")
elif tentativo > segreto:
print("Troppo alto!")
else:
print(f"Indovinato in {tentativi} tentativi!")
break
Spiegazione
import random carica il modulo; random.randint(1, 100) restituisce un intero tra 1 e 100, estremi inclusi.
Il ciclo while True continua all’infinito: quando l’utente indovina stampiamo il risultato ed usciamo con break.
Esercizio 6 – Statistiche su una lista
Livello: Intermedio · Argomenti: liste, funzioni built-in, split, list comprehension
Consegna: L’utente scrive una serie di numeri separati da spazi. Stampa quanti sono, il massimo, il minimo, la media, la lista ordinata e solo i numeri pari.
Teoria in breve: Una lista è una collezione ordinata e modificabile, scritta tra parentesi quadre. Python offre funzioni pronte come len, max, min, sum e sorted. Le list comprehension creano una nuova lista in una sola riga.
Soluzione
testo = input("Scrivi dei numeri separati da spazi: ")
numeri = [int(x) for x in testo.split()]
print("Quantità:", len(numeri))
print("Massimo:", max(numeri))
print("Minimo:", min(numeri))
print(f"Media: {sum(numeri) / len(numeri):.2f}")
print("Ordinati:", sorted(numeri))
pari = [n for n in numeri if n % 2 == 0]
print("Numeri pari:", pari)
Spiegazione
testo.split() divide la stringa negli spazi e restituisce una lista di stringhe, ad es. ['4', '9', '2'].
[int(x) for x in ...] è una list comprehension: per ogni elemento x calcola int(x) e raccoglie i risultati in una nuova lista. Con if alla fine possiamo anche filtrare, come facciamo per i numeri pari.
sorted() restituisce una nuova lista ordinata senza modificare l’originale (al contrario del metodo numeri.sort()).
Esercizio 7 – Numeri primi con le funzioni
Livello: Intermedio · Argomenti: def, return, parametri, ottimizzazione
Consegna: Scrivi una funzione e_primo(n) che restituisca True se n è primo, e una funzione primi_fino_a(limite) che restituisca la lista dei numeri primi fino a quel limite.
Teoria in breve: Le funzioni si definiscono con def, ricevono parametri e restituiscono un valore con return. Spezzare un problema in piccole funzioni rende il codice più chiaro, testabile e riutilizzabile.
Soluzione
def e_primo(n):
"""Restituisce True se n è un numero primo."""
if n < 2:
return False
for d in range(2, int(n ** 0.5) + 1):
if n % d == 0:
return False
return True
def primi_fino_a(limite):
return [n for n in range(2, limite + 1) if e_primo(n)]
limite = int(input("Trova i numeri primi fino a: "))
print(primi_fino_a(limite))
Spiegazione
Un numero è primo se è divisibile solo per 1 e per sé stesso. Basta controllare i divisori fino alla radice quadrata di n (n ** 0.5): se n avesse un divisore più grande, ne avrebbe anche uno più piccolo già controllato. Questo rende la funzione molto più veloce.
Appena troviamo un divisore usciamo subito con return False. La stringa tra triple virgolette è una docstring: documenta cosa fa la funzione.
primi_fino_a riutilizza e_primo dentro una list comprehension: ecco il vantaggio di scrivere funzioni piccole e componibili.
Esercizio 8 – Contare le parole di un testo
Livello: Avanzato · Argomenti: stringhe, dizionari, ordinamento con key
Consegna: Leggi un testo e stampa le 5 parole più frequenti con il numero di occorrenze, ignorando maiuscole e punteggiatura.
Teoria in breve: Un dizionario (dict) associa delle chiavi a dei valori, come una rubrica: {'ciao': 3}. È perfetto per contare le occorrenze. I metodi delle stringhe (lower, strip, split) aiutano a «pulire» il testo.
Soluzione
import string
testo = input("Incolla un testo: ")
conteggio = {}
for parola in testo.lower().split():
parola = parola.strip(string.punctuation)
if parola:
conteggio[parola] = conteggio.get(parola, 0) + 1
classifica = sorted(conteggio.items(), key=lambda coppia: coppia[1], reverse=True)
print("Le 5 parole più frequenti:")
for parola, volte in classifica[:5]:
print(f"{parola}: {volte}")
Spiegazione
testo.lower().split() porta tutto in minuscolo e divide in parole; strip(string.punctuation) elimina la punteggiatura all’inizio e alla fine di ogni parola.
conteggio.get(parola, 0) restituisce il valore attuale o 0 se la parola non c’è ancora: così non serve controllare a parte se la chiave esiste.
items() fornisce le coppie (parola, conteggio). sorted con key=lambda coppia: coppia[1] ordina in base al conteggio, reverse=True in ordine decrescente; [:5] prende i primi cinque elementi (slicing).
Nella libreria standard esiste anche collections.Counter, che fa tutto questo in una riga: Counter(parole).most_common(5).
Esercizio 9 – Rubrica salvata su file
Livello: Avanzato · Argomenti: file, JSON, with, menu interattivo
Consegna: Crea una piccola rubrica a menu che permetta di aggiungere un contatto, cercarlo, elencarli tutti e uscire. I contatti devono essere salvati in un file rubrica.json così da non perderli alla chiusura.
Teoria in breve: Per rendere i dati persistenti bisogna salvarli su file. Il blocco with open(...) apre il file e lo chiude automaticamente. Il modulo json converte dizionari e liste Python in testo e viceversa.
Soluzione
import json
import os
FILE = "rubrica.json"
def carica():
if os.path.exists(FILE):
with open(FILE, "r", encoding="utf-8") as f:
return json.load(f)
return {}
def salva(rubrica):
with open(FILE, "w", encoding="utf-8") as f:
json.dump(rubrica, f, indent=2, ensure_ascii=False)
rubrica = carica()
while True:
scelta = input("\n1) Aggiungi 2) Cerca 3) Elenca 4) Esci\nScelta: ")
if scelta == "1":
nome = input("Nome: ").strip()
telefono = input("Telefono: ").strip()
rubrica[nome] = telefono
salva(rubrica)
print("Contatto salvato.")
elif scelta == "2":
nome = input("Nome da cercare: ").strip()
print(rubrica.get(nome, "Contatto non trovato."))
elif scelta == "3":
for nome, telefono in sorted(rubrica.items()):
print(f"{nome}: {telefono}")
elif scelta == "4":
print("Arrivederci!")
break
else:
print("Scelta non valida.")
Spiegazione
carica() controlla con os.path.exists se il file esiste: se sì lo legge con json.load, altrimenti parte da un dizionario vuoto.
salva() apre il file in scrittura ("w") e lo riscrive con json.dump; indent=2 lo rende leggibile e ensure_ascii=False conserva le lettere accentate.
Il ciclo while True mostra il menu finché l’utente non sceglie 4. Ogni modifica viene salvata subito, così i dati restano anche dopo aver chiuso il programma.
Esercizio 10 – Conto bancario con le classi
Livello: Avanzato · Argomenti: classi, oggetti, metodi, eccezioni, ereditarietà
Consegna: Crea una classe ContoBancario con titolare e saldo, e i metodi deposita, preleva e la stampa del conto. Impedisci importi negativi e prelievi superiori al saldo sollevando eccezioni. Crea poi una sottoclasse ContoRisparmio che aggiunga gli interessi.
Teoria in breve: La programmazione a oggetti (OOP) raggruppa dati (attributi) e comportamenti (metodi) in una classe, il «progetto» da cui si creano gli oggetti. Le eccezioni segnalano situazioni di errore e si gestiscono con try / except. L’ereditarietà permette di creare una classe a partire da un’altra.
Soluzione
class ContoBancario:
def __init__(self, titolare, saldo=0.0):
self.titolare = titolare
self.saldo = saldo
def deposita(self, importo):
if importo <= 0:
raise ValueError("L'importo deve essere positivo")
self.saldo += importo
def preleva(self, importo):
if importo <= 0:
raise ValueError("L'importo deve essere positivo")
if importo > self.saldo:
raise ValueError("Saldo insufficiente")
self.saldo -= importo
def __str__(self):
return f"Conto di {self.titolare}: {self.saldo:.2f} €"
class ContoRisparmio(ContoBancario):
def __init__(self, titolare, saldo=0.0, tasso=0.02):
super().__init__(titolare, saldo)
self.tasso = tasso
def applica_interessi(self):
self.deposita(self.saldo * self.tasso)
conto = ContoRisparmio("Giulia", 1000)
conto.deposita(500)
conto.applica_interessi()
print(conto)
try:
conto.preleva(5000)
except ValueError as errore:
print("Operazione rifiutata:", errore)
Spiegazione
__init__ è il costruttore: viene eseguito quando si crea un oggetto e inizializza gli attributi. self rappresenta l’oggetto stesso.
deposita e preleva controllano i dati e, se qualcosa non va, usano raise per sollevare un’eccezione invece di lasciare il conto in uno stato sbagliato. __str__ definisce come l’oggetto viene stampato con print.
ContoRisparmio(ContoBancario) eredita tutti i metodi del conto base; super().__init__ richiama il costruttore del genitore e poi aggiungiamo il tasso. Il nuovo metodo applica_interessi riutilizza deposita.
Il blocco try / except intercetta l’errore del prelievo troppo alto e mostra un messaggio chiaro invece di far terminare il programma.
Consiglio: prova sempre a risolvere l’esercizio da solo prima di leggere la soluzione, poi modificalo per sperimentare: è il modo più veloce per imparare.
