Corso Python / Struttura dei package e punti d’ingresso con __main__
Un package raggruppa moduli collegati in una gerarchia. Racchiudere l’esecuzione del programma in main() e chiamarla da un punto d’ingresso esplicito permette ad altro codice di importare il modulo senza avviare automaticamente il programma.
Preparare la struttura di un package regolare
L’esempio usa un package regolare con __init__.py. Esistono anche namespace package che possono non avere quel file, ma qui manteniamo una struttura esplicita. Crea la cartella project e al suo interno expense_app con questi file:
project/
expense_app/
__init__.py
math_tools.py
__main__.py
Esegui i comandi dalla cartella project, che contiene expense_app, non dalla cartella interna del package. Come nel progetto di moduli e import, il nome e la disposizione dei file devono corrispondere ai riferimenti nel codice. Non servono librerie esterne.
La documentazione ufficiale di __main__ distingue il nome dell’ambiente di esecuzione dal file speciale usato come punto d’ingresso di un package.
Salvare tutti i file ed eseguire il package
Salva tutti i file in UTF-8. Il contenuto completo di expense_app/__init__.py è:
"""Expense calculation package."""
La docstring significa «Package per il calcolo delle spese». Non avvia alcun calcolo. In expense_app/math_tools.py inserisci:
def total(amounts):
return sum(amounts)
In expense_app/__main__.py inserisci:
from .math_tools import total
def main():
print(total([3500, 8000, 2500]))
if __name__ == "__main__":
main()
Dal terminale nella cartella project, con l’interprete del progetto selezionato, esegui:
python -m expense_app
Il risultato è 14000. Fuori dall’ambiente virtuale su macOS o Ubuntu puoi usare python3 -m expense_app se python3 è l’interprete scelto. -m cerca il package secondo le regole di importazione ed esegue il suo file __main__.py con il contesto del package.
Il punto in from .math_tools import total indica un import relativo allo stesso package. Eseguire invece python expense_app/__main__.py come semplice percorso di file non fornisce quel contesto e può generare ImportError per assenza di un package genitore noto. Per questo esercizio mantieni il comando con -m.
Controllare __name__ e gli effetti dell’import
Quando il modulo è il bersaglio dell’esecuzione, il suo nome speciale è __main__. Quando viene importato come modulo di un package, ha invece un nome come expense_app.__main__. Il controllo if __name__ == "__main__" usa questa differenza per chiamare main soltanto nel primo caso.
Main è una normale funzione: il suo nome non la esegue automaticamente. Le definizioni possono rimanere al livello principale, mentre calcoli, richieste di input e modifiche ai file vengono rinviati alla chiamata. Il controllo non annulla però altre istruzioni eseguibili che tu lasci fuori dal blocco: anche __init__.py deve evitare avvii involontari se desideri un import senza operazioni applicative.
La ricerca di un modulo e quella di un file di dati non sono la stessa cosa. Un percorso relativo come data.csv si riferisce normalmente alla cartella di lavoro corrente; non diventa automaticamente relativo al package solo perché avvii Python con -m. Per programmi distribuiti valuta argomenti di percorso espliciti o le funzioni per le risorse del package, secondo il tipo di dato.
Verificare separatamente esecuzione e riuso
Lascia invariati i sorgenti e, sempre da project, esegui anche questo comando:
python -c "import expense_app.__main__; print('imported')"
Deve comparire soltanto imported. Non deve apparire 14000: l’import definisce main ma non la chiama. Se il totale compare, controlla se una chiamata o una stampa applicativa è rimasta al livello principale fuori dal controllo di __name__.
- Avvia il package con
python -m expense_appe verifica 14000. - Esegui il comando di importazione e verifica il solo imported.
- Confronta con l’esecuzione diretta del file interno, mantenendo il codice invariato, e riconosci l’errore relativo al package genitore. Torna poi al comando con -m.
Se rinomini una cartella o un modulo, aggiorna anche import e comando di avvio. Preparare questa struttura di sorgenti non equivale a pubblicare una distribuzione installabile: quel passaggio richiede metadati come pyproject.toml, costruzione degli archivi e verifica dell’installazione. La guida ai package Python chiarisce l’organizzazione dei moduli su cui si basa l’esempio.




