Lezione avanzata di Python: funzioni, moduli e gestione avanzata dei dati

Lezione avanzata di Python: scopri come scrivere funzioni efficienti, gestire moduli e librerie, utilizzare strutture dati complesse e prepararti per progetti reali.

Benvenuti a questa nuova lezione di Python: dopo aver affrontato le basi – come variabili, operatori, controllo del flusso, strutture dati e cicli – è arrivato il momento di fare un passo avanti. Questa lezione vi guiderà attraverso funzioni avanzate, moduli e librerie, gestione di strutture dati complesse e l’implementazione di piccoli progetti reali per consolidare le competenze.

Lo scopo è quello di prepararti ad applicare Python non solo per semplici script, ma anche per sviluppi più articolati, automatizzazioni, elaborazione dati e workflow ripetibili. Se hai letto i precedenti articoli relativi ai cicli (“Cicli for e while…”), alle strutture di controllo (“If, For, While spiegati”), e alle strutture dati (“liste, tuple, set e dizionari”), allora sei pronto per questa fase intermedia-avanzata.

1. Funzioni in Python: dal semplice al modulare

In Python, le funzioni rappresentano blocchi di codice che possono essere riutilizzati, semplificando lo sviluppo e migliorando la leggibilità. In questa sezione vedremo come definire funzioni semplici, passare parametri, usare valori di ritorno, applicare funzioni a più parametri, utilizzare argomenti opzionali e, soprattutto, come progettare funzioni modulari che possano essere riutilizzate all’interno di moduli più grandi.

1.1 Definizione di base e parametri

Una funzione si definisce con la parola chiave def, seguita dal nome della funzione e da una coppia di parentesi. Ecco un esempio semplice:

def saluta(nome):  
    print(f"Benvenuto, {nome}!")  

Qui la funzione saluta prende un argomento nome e stampa un messaggio. Puoi chiamarla così:

saluta("Maria")  # Benvenuto, Maria!  

Possiamo anche restituire un valore:

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

risultato = somma(3, 5)  
print(risultato)  # 8  

1.2 Argomenti opzionali e valori di default

Le funzioni possono avere parametri con valori predefiniti, rendendo alcuni argomenti opzionali:

def saluta(nome="Amico", messaggio="Benvenuto"):  
    print(f"{messaggio}, {nome}!")  

saluta()             # Benvenuto, Amico!  
saluta("Luca")       # Benvenuto, Luca!  
saluta("Luca","Ciao")# Ciao, Luca!  

Questo rende le funzioni più flessibili e applicabili in diversi contesti.

1.3 Funzioni con numero variabile di argomenti

In Python è possibile definire funzioni che accettano un numero variabile di argomenti usando *args e **kwargs:

def stampa_tutti(*args):  
    for arg in args:  
        print(arg)  

def dettaglio(**kwargs):  
    for key, value in kwargs.items():  
        print(f"{key} = {value}")  

stampa_tutti(1, 2, 3, "ciao")  
# 1  
# 2  
# 3  
# ciao  

dettaglio(nome="Anna", età=30)  
# nome = Anna  
# età = 30  

Queste funzioni sono molto utili nei casi in cui non si conosce in anticipo il numero di parametri da passare, oppure quando si vuole trasferire argomenti a un’altra funzione.

1.4 Funzioni anonime (lambda) e funzioni come oggetti

In Python, le funzioni sono a tutti gli effetti oggetti di prima classe. Ciò significa che possiamo assegnarle a variabili, passarle come argomenti e restituirle da altre funzioni. Le funzioni anonime – definite con la parola chiave lambda – sono utili per operazioni rapide e inline:

quad = lambda x: x * x  
print(quad(5))  # 25  

def operazione(f, x):  
    return f(x)  

print(operazione(lambda y: y + 10, 7))  # 17  

Questo approccio apre la strada alla programmazione funzionale in Python, spesso utile nella manipolazione di collezioni di dati.

1.5 Modularizzazione e importazione di funzioni

Quando il progetto cresce, è importante suddividere il codice in moduli separati e importabili. Si crea un file Python (ad es. utils.py) con funzioni che vogliamo riutilizzare:

# utils.py  
def saluta(nome):  
    print(f"Benvenuto, {nome}!")  

# main.py  
import utils  

utils.saluta("Marco")  

In alternativa, possiamo importare singole funzioni:

from utils import saluta  

saluta("Beatrice")  

In questo modo il codice risulta più organizzato, modulare e manutenibile.

2. Moduli e librerie standard in Python

Uno dei punti di forza di Python è la quantità e qualità delle librerie incluse nella distribuzione standard, e la facilità con cui installare librerie esterne. In questa sezione esploreremo alcune librerie standard fondamentali, l’installazione di pacchetti esterni con pip e come progettare i propri moduli.

2.1 Lavorare con i moduli standard

Python viene fornito con numerosi moduli integrati, per esempio:

  • os – per interagire con il sistema operativo;
  • sys – per accedere a variabili e funzioni dell’interprete;
  • math – funzioni matematiche;
  • datetime – gestire date e orari;
  • json – serializzazione/deserializzazione JSON.

Ecco un esempio d’uso:

import os  
print(os.getcwd())  # mostra la cartella corrente  

import datetime  
oggi = datetime.datetime.now()  
print(oggi.strftime("%Y-%m-%d %H:%M:%S"))  

2.2 Installazione di pacchetti esterni con pip

Per estendere le capacità di Python, si utilizzano pacchetti esterni installabili tramite il comando:

pip install nome_pacchetto  

Ad esempio, per installare requests (per le richieste HTTP):

pip install requests  

Una volta installato, si può importare ed utilizzare facilmente:

import requests  
response = requests.get("https://api.example.com/data")  
print(response.json())  

2.3 Creare e distribuire il proprio modulo

Supponiamo di voler creare un modulo personalizzato mio_modulo.py:

# mio_modulo.py  
def saluta(nome):  
    return f"Ciao, {nome}!"  

# utilizzo in un altro file  
import mio_modulo  
print(mio_modulo.saluta("Luca"))  # Ciao, Luca!  

Per rendere disponibile il modulo anche su altri sistemi, è possibile impacchettarlo e pubblicarlo su PyPI (Python Package Index) seguendo le best-practice ufficiali.

3. Strutture dati avanzate e manipolazione

Abbiamo già affrontato le strutture dati base – liste, tuple, set e dizionari – ma in questa lezione esploreremo come gestire collezioni più complesse, annidate, e come manipolarle efficacemente con comprensioni, generatori e iteratori.

3.1 Comprensioni di lista, set e dizionario

Le comprensioni consentono di costruire nuove collezioni in modo compatto e leggibile. Ecco alcuni esempi:

numeri = [1, 2, 3, 4, 5]  
quad = [x * x for x in numeri]  
print(quad)  # [1, 4, 9, 16, 25]  

frutta = ["mela","banana","ciliegia"]  
frutta_iniziale = {f[0] for f in frutta}  
print(frutta_iniziale)  # {'m','b','c'}  

nome_pi = {"nome":"Paolo","età":30}  
upper_keys = {k.upper():v for k,v in nome_pi.items()}  
print(upper_keys)  # {'NOME':'Paolo','ETÀ':30}  

3.2 Generatori e iteratori personalizzati

I generatori permettono di produrre sequenze “lazy”, ovvero generate su richiesta, e sono utili quando lavoriamo con grandi dataset. Ecco un semplice generatore:

def conta_fino(n):  
    i = 1  
    while i <= n:  
        yield i  
        i += 1  

for num in conta_fino(5):  
    print(num)  
# 1  
# 2  
# 3  
# 4  
# 5  

È anche possibile creare speciali iteratori estendendo la classe __iter__ e __next__:

class MioContatore:  
    def __init__(self, massimo):  
        self.massimo = massimo  
        self.conta = 0  

    def __iter__(self):  
        return self  

    def __next__(self):  
        if self.conta < self.massimo:  
            self.conta += 1  
            return self.conta  
        else:  
            raise StopIteration  

for i in MioContatore(3):  
    print(i)  
# 1  
# 2  
# 3  

3.3 Manipolazione avanzata dei dizionari annidati

Quando lavoriamo con dati strutturati – ad esempio JSON complessi – è utile gestire dizionari annidati. Ecco un esempio di funzione che “appiattisce” un dizionario:

def appiattisci_dict(d, separatore='_', prefisso=''):  
    elementi = {}  
    for k, v in d.items():  
        nuovo_nome = f"{prefisso}{k}" if prefisso else k  
        if isinstance(v, dict):  
            elementi.update(appiattisci_dict(v, separatore, f"{nuovo_nome}{separatore}"))  
        else:  
            elementi[nuovo_nome] = v  
    return elementi  

dati = {  
    "utente": {"nome":"Alice","info":{"età":28,"città":"Milano"}},  
    "ruolo":"editor"  
}  
print(appiattisci_dict(dati))  
# {'utente_nome':'Alice','utente_info_età':28,'utente_info_città':'Milano','ruolo':'editor'}  

4. Progetto pratico: Automatizzare un report via Python

Adesso mettiamo insieme quanto appreso per costruire un piccolo progetto pratico: un report automatico che legge file CSV, filtra dati, genera statistiche e salva un output in formato Excel o PDF. Questo tipo di attività è molto comune nelle startup, nel marketing o nell’analisi dati.

4.1 Requisiti e librerie necessarie

Prima di iniziare, installa le librerie necessarie:

pip install pandas openpyxl matplotlib  

Qui sono coinvolte:

  • pandas – per la manipolazione di dati tabellari;
  • openpyxl – per generare file Excel;
  • matplotlib – per realizzare grafici.

4.2 Caricare e filtrare i dati

Supponiamo di avere un file vendite.csv con colonne: data, prodotto, quantità, prezzo_unitario, regione. Ecco come leggere e filtrare i dati:

import pandas as pd  

df = pd.read_csv("vendite.csv", parse_dates=["data"])  
# Filtriamo le vendite della regione “Nord” dopo il 2024-01-01  
filtro = (df["regione"] == "Nord") & (df["data"] >= "2024-01-01")  
df_filtrato = df.loc[filtro]  

4.3 Generare statistiche e grafici

Calcoliamo alcune statistiche e generiamo un grafico di esempio:

# Totale vendite per prodotto  
df_filtrato["totale"] = df_filtrato["quantità"] * df_filtrato["prezzo_unitario"]  
statistiche = df_filtrato.groupby("prodotto")["totale"].sum().reset_index()  

import matplotlib.pyplot as plt  

plt.figure(figsize=(8,5))  
plt.bar(statistiche["prodotto"], statistiche["totale"], color="skyblue")  
plt.title("Totale vendite per prodotto – Regione Nord")  
plt.xlabel("Prodotto")  
plt.ylabel("Totale (€)")  
plt.tight_layout()  
plt.savefig("vendite_prodotti_nord.png")  

4.4 Salvare l’output in Excel

import openpyxl  

# Salviamo il DataFrame in un file Excel  
df_filtrato.to_excel("report_vendite_nord.xlsx", index=False)  

4.5 Automatizzare l’intero processo

Infine, possiamo costruire uno script Python che esegue tutti i passaggi in sequenza:

import pandas as pd  
import matplotlib.pyplot as plt  

def carica_dati(file_csv):  
    return pd.read_csv(file_csv, parse_dates=["data"])  

def filtra_dati(df, regione, data_minima):  
    return df.loc[(df["regione"] == regione) & (df["data"] >= data_minima)]  

def genera_statistiche(df):  
    df["totale"] = df["quantità"] * df["prezzo_unitario"]  
    return df.groupby("prodotto")["totale"].sum().reset_index()  

def crea_grafico(statistiche, nome_file):  
    plt.figure(figsize=(8,5))  
    plt.bar(statistiche["prodotto"], statistiche["totale"], color="skyblue")  
    plt.title(f"Totale vendite per prodotto – {statistiche.shape[0]} prodotti")  
    plt.xlabel("Prodotto")  
    plt.ylabel("Totale (€)")  
    plt.tight_layout()  
    plt.savefig(nome_file)  

def salva_excel(df, nome_file):  
    df.to_excel(nome_file, index=False)  

def main():  
    df = carica_dati("vendite.csv")  
    df_f = filtra_dati(df, "Nord", "2024-01-01")  
    stat = genera_statistiche(df_f)  
    crea_grafico(stat, "grafico_vendite.png")  
    salva_excel(df_f, "report_vendite_nord.xlsx")  

if __name__ == "__main__":  
    main()  

Con questo script, è possibile eseguire in modo automatizzato un report completo – carico dati, filtraggio, calcolo, creazione grafico e salvataggio. Un ottimo esercizio per mettere in pratica funzioni, moduli, librerie e strutture dati avanzate.

5. Best practice e consigli per progetti reali

Quando si lavora su progetti reali con Python, è bene tenere a mente alcune best practice che migliorano qualità, manutenibilità e scalabilità del codice.

5.1 Organizzazione del progetto

Struttura il progetto in modo chiaro:

project/  
│– src/  
│   │– __init__.py  
│   │– main.py  
│   │– utils.py  
│– data/  
│   │– vendite.csv  
│– outputs/  
│   │– report_vendite_nord.xlsx  
│   │– grafico_vendite.png  
│– requirements.txt  
│– README.md  

In questo modo si separano i sorgenti, i dati, gli output e la documentazione.

5.2 Ambiente virtuale e dipendenze

Usa un ambiente virtuale per isolare le dipendenze del progetto:

python -m venv env  
source env/bin/activate  # su Linux/Mac  
env\Scripts\activate     # su Windows  
pip install pandas openpyxl matplotlib  
pip freeze > requirements.txt  

Questo aiuta a mantenere coerenza e facilità di distribuzione.

5.3 Testing e documentazione

Anche per piccoli script è utile scrivere test e documentazione:

# test_utils.py  
from utils import saluta  

def test_saluta():  
    assert saluta("Luca") == "Ciao, Luca!"  

Documenta le funzioni con docstring:

def saluta(nome):  
    """  
    Restituisce un saluto personalizzato.  
    :param nome: stringa con il nome della persona  
    :return: stringa di saluto  
    """  
    return f"Ciao, {nome}!"  

5.4 Logging e gestione errori

Per applicazioni reali, gestire eccezioni e registrare i log è fondamentale:

import logging  

logging.basicConfig(filename='app.log', level=logging.INFO)  

def carica_dati(file_csv):  
    try:  
        df = pd.read_csv(file_csv, parse_dates=["data"])  
        logging.info(f"Dati caricati da {file_csv}")  
        return df  
    except Exception as e:  
        logging.error(f"Errore caricamento dati: {e}")  
        raise  

Questo migliora la robustezza e la tracciabilità dell’applicazione.

6. Conclusione e passaggi successivi

In questa lezione abbiamo affrontato aspetti intermedi-avanzati di Python: funzioni modulare, moduli e librerie standard/esterni, strutture dati avanzate e un progetto pratico per automatizzare un report. Ora sei in grado di progettare script più complessi e organizzati.

I passaggi successivi per consolidare le competenze sono:

  • Creare un piccolo progetto personale (es. analisi dati, automazione, web scraping) utilizzando funzioni e moduli;
  • Scoprire librerie più specializzate (ad esempio numpy, scipy, requests, beautifulsoup4);
  • Implementare test automatici, documentazione e packaging del proprio modulo;
  • Ottimizzare prestazioni, gestire eccezioni, usare logging;
  • Condividere il progetto su GitHub o PyPI per esercitarsi anche nella distribuzione del codice.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.