Lezione 13: Definire funzioni con def ✅

In questa lezione impari a definire funzioni in Python con la parola chiave def, a passare parametri (posizionali e per parola‑chiave), a usare return per restituire valori e a scrivere esempi pratici chiari e riusabili. Capirai perché le funzioni rendono il codice più leggibile, testabile e manutenibile, come documentarle con docstring e come evitare errori comuni (es. confondere print() con return).


🔗 Ripassi consigliati (interni):
• Lezione 2 – Il tuo primo script: Hello, Python! → https://www.saperesemplice.com/primo-script-python-hello/
• Lezione 3 – Variabili e Tipi di Dato → https://www.saperesemplice.com/variabili-tipi-dato-python/
• Lezione 9 – Ciclo for e range() → https://www.saperesemplice.com/ciclo-for-range-python/
• Lezione 6 – Condizioni con if, else, elif → https://www.saperesemplice.com/condizioni-if-else-elif-python/


Dettagli lezione

⏱️ Tempo stimato: 35–45 minuti
🧩 Difficoltà: Facile
🎯 Obiettivi specifici

  • Definire funzioni con def nome(par1, par2, ...) e comprendere parametri vs argomenti.
  • Usare return per restituire valori (incluso ritorno di più valori tramite tuple).
  • Comprendere parametri posizionali e per parola‑chiave (keyword arguments).
  • Scrivere funzioni pure e riusabili con docstring e nomi snake_case.
  • Applicare i concetti in esempi pratici e un esercizio step‑by‑step.

🧱 Perché definire funzioni

Le funzioni incapsulano istruzioni in un blocco riutilizzabile. Vantaggi:

  • Riuso: scrivi una volta, usi ovunque.
  • Astrazione: nascondi i dettagli, esponi un nome chiaro.
  • Testabilità: puoi testare la funzione isolatamente.
  • Ordine: il codice è più corto e leggibile.
# Sintassi base
# def <nome>(<parametri>...):
#     <blocco>
#     return <valore_opzionale>

def greet(name):
    """Return a welcome message for the given name."""
    return f"Hello, {name}!"

print(greet("Alice"))  # Hello, Alice!

💡 Usa snake_case per i nomi delle funzioni: calculate_total, send_email. Evita nomi generici come do().

Domanda per te: quale funzionalità ripeti spesso nei tuoi script e potresti incapsulare in una funzione?


🔎 Parametri e argomenti

Parametro: il nome nella definizione della funzione.

  • Argomento: il valore passato in chiamata.
def area_rect(width, height):
    return width * height

# Argomenti posizionali
print(area_rect(3, 5))      # 15

# Argomenti per parola‑chiave (keyword)
print(area_rect(height=5, width=3))  # 15 (ordine libero)

✨ Suggerimento leggibilità: quando una funzione ha molti parametri, privilegia chiamate per parola‑chiave: send_email(to="...", subject="...").

Errore comune da evitare

Passare lo stesso argomento due volte (posizionale e keyword): f(1, x=1) → TypeError: got multiple values for argument 'x'.


🔁 return: restituire valori (anche multipli)

  • return <expr> termina la funzione e restituisce il valore.
  • return senza espressione restituisce None.
  • Se non scrivi return, Python restituisce None alla fine.
def divide(a, b):
    if b == 0:
        return None  # oppure: raise ZeroDivisionError
    return a / b

result = divide(10, 2)
print(result)  # 5.0

Ritornare più valori

In Python è naturale restituire più valori come tupla e spacchettarli con l’unpacking.

def stats(numbers):
    total = sum(numbers)
    count = len(numbers)
    average = total / count if count else 0
    return total, count, average  # tuple

s, n, avg = stats([3, 7, 10])
print(s, n, avg)  # 20 3 6.666...

Domanda per te: quando preferiresti restituire un dizionario invece di una tupla?


🧰 Documentare le funzioni con docstring

Una docstring è una stringa all’inizio del corpo della funzione che spiega cosa fa, parametri, ritorno e note.

def fahrenheit_to_celsius(f):
    """Convert Fahrenheit to Celsius.

    Args:
        f (float): Temperature in Fahrenheit.
    Returns:
        float: Temperature in Celsius.
    """
    return (f - 32) * 5/9

print(fahrenheit_to_celsius.__doc__.splitlines()[0])  # Convert Fahrenheit to Celsius.

🧑‍🏫 Best practice: una riga di sommario concisa; per docstring lunghe, una riga vuota e poi i dettagli.


🔶 Esempi pratici

1) Normalizza un nome (strip, title)

def normalize_name(raw):
    """Return a cleaned display name."""
    return raw.strip().title()

print(normalize_name("  aLiCe  "))  # Alice

2) Somma dei numeri da 1 a n

def sum_to(n):
    """Return the sum from 1 to n inclusive."""
    total = 0
    for i in range(1, n + 1):
        total += i
    return total

print(sum_to(5))  # 15

3) Formatta un prezzo con valuta

def format_price(amount, currency="€"):
    """Return a formatted price like '€12.50'."""
    return f"{currency}{amount:.2f}"

print(format_price(12.5))      # €12.50
print(format_price(12.5, "$"))  # $12.50

4) Validazione semplice (ritorno booleano + messaggio)

def is_strong_password(pwd):
    """Return True if password meets minimal criteria, else False."""
    return len(pwd) >= 8 and any(c.isdigit() for c in pwd)

if is_strong_password("abc12345"):
    print("Password accepted")
else:
    print("Weak password")

💬 Domanda per il lettore: nelle funzioni 2–4, dove useresti parametri per parola‑chiave per aumentare la chiarezza della chiamata?


🧠 print() non è return: cosa cambia?

  • print() mostra un testo (effetto collaterale), non restituisce il risultato da riusare.
  • return passa un valore al chiamante che può essere salvato, testato, combinato.
def bad_add(a, b):
    print(a + b)   # side-effect only

x = bad_add(2, 3)  # stampa 5 ma x è None
print(x)            # None

# ✔️ Corretto

def add(a, b):
    return a + b

x = add(2, 3)
print(x)            # 5

Errore comune da evitare

Scrivere funzioni che stampano invece di ritornare. Preferisci return e lascia al chiamante la responsabilità di stampare.


🧭 Parametri facili da leggere: consigli veloci

  • Dai nomi parlanti ai parametri (width, height, tax_rate).
  • Evita di riusare nomi di built‑in (list, sum, dict).
  • Ordina i parametri: prima quelli obbligatori, poi quelli opzionali (verranno approfonditi nella prossima lezione).
  • Usa valori di ritorno chiari: True/False, numeri, tuple o dict ben strutturati.

⚠️ Errore comune da evitare

Definire parametri opzionali con oggetti mutabili come valore di default (es. def f(x, items=[])). Questo porta a stato condiviso tra chiamate.
Soluzione tipica: usa None come default e crea l’oggetto dentro la funzione. (Approfondiremo nella prossima lezione.)

# ❌ Sbagliato
# def append_user(user, users=[]):
#     users.append(user)
#     return users

# ✔️ Corretto
def append_user(user, users=None):
    if users is None:
        users = []
    users.append(user)
    return users

📖 Sezione teorica

  • Definizione: def nome(par1, ...) + blocco indentato; primo statement opzionale: docstring.
  • Parametri/Argomenti: posizionali o keyword; non passare lo stesso parametro due volte.
  • return: termina la funzione; senza espressione o a fine funzione → None.
  • Ritorni multipli: usa tuple e unpacking.
  • Stampa vs ritorno: print() mostra, return restituisce.
  • Stile: nomi snake_case, docstring (PEP 257), leggibilità prima di tutto.

🔗 Riferimenti esterni:
• Python Tutorial – Defining Functions → https://docs.python.org/3/tutorial/controlflow.html#defining-functions
• PEP 8 – Style Guide (naming snake_case) → https://peps.python.org/pep-0008/
• PEP 257 – Docstring Conventions → https://peps.python.org/pep-0257/


📚 Glossario tecnico

  • Parametro: nome formale nella definizione della funzione.
  • Argomento: valore passato alla funzione in chiamata.
  • Docstring: stringa di documentazione all’inizio del corpo della funzione.

📝 Esercizio finale

Realizza una piccola libreria di utilità con 3 funzioni e un main di prova.

Requisiti:

  1. clean_email(s) → restituisce l’email normalizzata (trim, minuscolo) o None se non contiene @.
  2. apply_discount(price, rate) → ritorna price scontato del rate (0–1).
  3. summary(nums) → ritorna tuple (count, minimum, maximum, mean); se la lista è vuota, ritorna (0, None, None, 0).
  4. Nel main, chiama le funzioni e stampa in inglese un mini‑report.

Schema d’aiuto:

def clean_email(s):
    # Your code here...
    ...

def apply_discount(price, rate):
    ...

def summary(nums):
    ...

# Demo
print("-- Demo --")
print(clean_email("  [email protected]  "))
print(apply_discount(100, 0.2))
print(summary([3, 7, 10]))

🧠 Quiz finale

Non guardare le risposte subito! Sono più sotto 👇

  1. Cosa fa la parola chiave def?
    A) Crea una variabile
    B) Definisce una funzione
    C) Converte una tupla in lista
  2. Cosa restituisce una funzione senza return esplicito?
    A) 0
    B) Stringa vuota
    C) None
  3. Quale chiamata è corretta con argomenti keyword?
    A) area_rect(3, 5)
    B) area_rect(width=3, height=5)
    C) area_rect(3, height=5, width=3)
  4. Cosa non dovresti fare nei nomi funzione?
    A) Usare snake_case
    B) Usare nomi di built‑in come list
    C) Scrivere verbi/azioni (load_data)
  5. La differenza tra print() e return è che:
    A) print restituisce il valore al chiamante
    B) return stampa un valore
    C) print mostra, return restituisce
  6. Quale opzione descrive correttamente un parametro?
    A) Il valore passato in chiamata
    B) Il nome formale nella definizione
    C) Una variabile globale
  7. Qual è il modo pitonico per ritornare più valori?
    A) Lista
    B) Tupla (con eventuale unpacking)
    C) Stringa con separatore
  8. Quale riga causa TypeError: got multiple values for argument?
    A) f(x=1)
    B) f(1)
    C) f(1, x=1)

❓ FAQ

1) Posso definire una funzione dopo averla chiamata?
Il file viene eseguito top‑down: la funzione deve essere definita prima che il flusso arrivi alla chiamata. In REPL puoi definire e poi chiamare liberamente.

2) È meglio restituire una tupla o un dizionario?
Dipende: tupla per pochi valori con ordine chiaro; dizionario per etichette esplicite ({"sum": 10, "avg": 5}).

3) Le funzioni possono modificare gli argomenti?
Gli oggetti mutabili (liste, dict) possono essere modificati in‑place. Valuta se è desiderabile o se è meglio restituire una copia

4) Come scelgo i nomi delle funzioni?
Usa verbi chiari in snake_case (PEP 8). Esempi: load_data, normalize_text, send_email.

5) Come scrivo docstring efficaci?
Una riga di sommario, poi dettagli (parametri, ritorni, eccezioni). Vedi PEP 257.



Esterni
• Defining Functions – Python Tutorial → https://docs.python.org/3/tutorial/controlflow.html#defining-functions
• PEP 8 – Style Guide (naming) → https://peps.python.org/pep-0008/
• PEP 257 – Docstring Conventions → https://peps.python.org/pep-0257/

SapereSemplice
• Lezione 2 – Primo script: Hello, Python → https://www.saperesemplice.com/primo-script-python-hello/
• Lezione 3 – Variabili e Tipi di Dato → https://www.saperesemplice.com/variabili-tipi-dato-python/
• Lezione 9 – Ciclo for e range → https://www.saperesemplice.com/ciclo-for-range-python/
• Lezione 6 – Condizioni con if/else/elif → https://www.saperesemplice.com/condizioni-if-else-elif-python/


🔁 Collegamento alla lezione successiva

➡️ Prosegui alla Lezione 14: Argomenti opzionali, scope e funzioni annidate
(Il link verrà inserito non appena l’articolo sarà online.)


🗨️ Hai dubbi? Lascia un commento!
📩 Vuoi il PDF della lezione? Scrivici!
🔗 Condividi con un compagno di corso.


🧪 Risposte del quiz

  1. B
  2. C
  3. B
  4. B
  5. C
  6. B
  7. B
  8. C

📅 Aggiornato il 04/09/2025

Lascia un commento