Moduli e pacchetti Python: import, pip, venv e librerie in parole semplici
Un modulo in Python è un file .py, un pacchetto è una cartella di moduli, una libreria è un insieme di pacchetti che si importano con la parola import. Le librerie esterne si installano con pip (o uv) in un ambiente virtuale venv, cioè una cartella isolata con interprete e pacchetti di un solo progetto, così le versioni di progetti diversi non entrano in conflitto.
Un modulo in Python è un normale file con del codice, e import è il modo per prendere da esso funzioni e classi. I moduli si raggruppano in pacchetti, i pacchetti formano le librerie, e per evitare che le librerie di progetti diversi si intralcino si installano in un ambiente virtuale venv. Vediamo l'intera catena: che cosa succede durante un import, come è fatto un pacchetto, come funzionano pip e venv in Python e quali sono i lazy import introdotti nella Python 3.15.
Modulo, pacchetto, libreria: qual è la differenza
| Concetto | Che cos'è | Esempio |
|---|---|---|
| Modulo | Un singolo file .py; il nome del modulo è il nome del file senza estensione | categorie.py → import categorie |
| Pacchetto | Cartella con moduli e il file __init__.py | spese/report.py → from spese.report import build |
| Libreria | Un insieme pronto di moduli e pacchetti per un certo compito | requests, pandas, la standard pathlib |
| Distribuzione (pacchetto PyPI) | Archivio installato da pip | pip install requests |
Nel parlato «libreria», «pacchetto» e «modulo» vengono spesso confusi, ed è normale. L'importante è sapere che tecnicamente import carica sempre un modulo o un pacchetto, mentre pip installa una distribuzione da PyPI, all'interno della quale ci sono i pacchetti.
Come funziona import in Python
Esistono diverse forme di import, e tutte compaiono nel codice reale:
import math # intero modulo, uso come math.sqrt
from math import sqrt, pi # singoli nomi
import datetime as dt # alias
from collections import Counter as C
print(math.sqrt(16), sqrt(9), pi)
print(dt.date.today())
Gli alias sono consolidati per le librerie grandi: import numpy as np, import pandas as pd. La forma from modulo import * è meglio evitarla: riempie lo spazio dei nomi e nasconde la provenienza dei nomi.
Che cosa fa davvero import
- Cerca il modulo nella cache
sys.modules. Se è già caricato, lo restituisce e basta. - Se non c'è, cerca il file nelle cartelle elencate in
sys.path: la cartella dello script avviato, la libreria standard, il site-packages dell'ambiente. - Esegue il file trovato per intero, dall'alto verso il basso.
- Mette l'oggetto modulo nella cache e lo associa al nome.
Da questo derivano due conseguenze pratiche. Un secondo import dello stesso modulo costa quasi nulla: il codice non viene eseguito di nuovo. In compenso, tutto il codice al livello più alto del modulo, compresi print e i calcoli pesanti, viene eseguito già durante l'import.
import sys
for p in sys.path:
print(p)
print("math" in sys.modules)
if __name__ == "__main__"
Poiché l'import esegue il file, serve un modo per distinguere «sono stato avviato come script» da «sono stato importato». Lo fa la variabile __name__: quando il file viene eseguito è uguale a "__main__", quando viene importato è uguale al nome del modulo.
# categorie.py
def guess(title: str) -> str:
return "cibo" if "bar" in title.lower() else "altro"
if __name__ == "__main__":
print(guess("Bar vicino a casa")) # viene eseguito solo con python categorie.py
Per un pacchetto esiste un'alternativa: un file __main__.py al suo interno e l'avvio con python -m spese.
Moduli standard di Python
Python arriva «con le batterie incluse»: centinaia di moduli integrati sono disponibili subito, senza installazione. I più usati:
| Modulo | A che cosa serve |
|---|---|
os, sys, pathlib, shutil | Sistema, percorsi, file |
json, csv, pickle | Formati di dati |
datetime, time, calendar | Data e ora |
math, random, statistics, fractions | Numeri |
re, string | Lavoro con il testo |
collections, itertools, functools | Strutture dati e iteratori |
logging, argparse, subprocess | Log, argomenti da riga di comando, avvio di programmi |
asyncio, threading | Asincronia e thread |
L'elenco completo dei moduli della libreria standard è in sys.stdlib_module_names (Python 3.10+). Per vedere che cosa contiene un modulo si usano dir(math) e help(math).
Un pacchetto proprio: struttura del progetto
Quando i moduli diventano molti, si raccolgono in un pacchetto. Una struttura molto diffusa è lo src-layout:
spese-progetto/
├── pyproject.toml
└── src/
└── spese/
├── __init__.py
├── cli.py
├── parsing.py
├── categorie.py
└── report.py
All'interno del pacchetto i moduli si importano a vicenda con il nome del pacchetto (from spese.categorie import guess) oppure in modo relativo (from .categorie import guess). L'import relativo funziona solo dentro il pacchetto, non in un file avviato direttamente come script.
pip in Python: installazione delle librerie
Le librerie di terze parti si trovano nel catalogo PyPI e si installano con pip, che è incluso in Python. Conviene richiamarlo tramite l'interprete: così la libreria finisce proprio nel Python con cui eseguite il codice.
python -m pip install requests # installare
python -m pip install "pandas>=2.3" # con limite di versione
python -m pip install --upgrade requests
python -m pip uninstall requests # rimuovere
python -m pip list # elenco dei pacchetti installati
python -m pip show requests # versione e dipendenze
python -m pip freeze > requirements.txt
python -m pip install -r requirements.txt
Su Windows al posto di python si usa spesso il launcher py. Se pip non si trova, lo installa il modulo integrato: python -m ensurepip --upgrade.
Librerie Python più diffuse
Su PyPI ci sono centinaia di migliaia di pacchetti, ma nella maggior parte dei progetti compare sempre la stessa breve lista. Conviene partire da essa, invece di cercare soluzioni esotiche.
| Compito | Librerie |
|---|---|
| Richieste HTTP e API | requests, httpx |
| Analisi dei dati e tabelle | pandas, numpy |
| Grafici | matplotlib |
| Servizi web | fastapi, django, flask |
| Test | pytest |
| Immagini | pillow (si importa come PIL) |
Attenzione: il nome per l'installazione e quello per l'import non sempre coincidono. Si installa pip install pillow, ma si scrive from PIL import Image; si installa opencv-python, ma si importa cv2. Se import fallisce subito dopo l'installazione, controllate nella documentazione del pacchetto con quale nome viene importato.
Installazione di librerie in Linux
Nelle versioni recenti di Debian e Ubuntu il comando pip install nel Python di sistema termina con l'errore externally-managed-environment (PEP 668). È una protezione: il Python di sistema appartiene al gestore pacchetti del sistema operativo e pip non deve romperlo. La soluzione giusta non è --break-system-packages, ma un ambiente virtuale.
venv in Python: ambiente virtuale
L'ambiente virtuale è una cartella separata con una propria copia dell'interprete (un collegamento ad esso) e un proprio site-packages. Le librerie installate lì sono visibili solo a quel progetto. Così un progetto può usare Django 4 e un altro Django 5 senza conflitti.
Il modulo venv è integrato in Python e non va installato a parte (su Debian/Ubuntu a volte serve il pacchetto di sistema python3-venv).
# creare l'ambiente nella cartella .venv
python -m venv .venv
# attivare: Linux e macOS
source .venv/bin/activate
# attivare: Windows PowerShell
.venv\Scripts\Activate.ps1
# ora pip installa i pacchetti dentro .venv
python -m pip install requests
python -c "import sys; print(sys.prefix)"
# uscire dall'ambiente
deactivate
Dopo l'attivazione all'inizio della riga del terminale compare (.venv). La cartella dell'ambiente non va salvata in git (aggiungete .venv/ a .gitignore) e non si copia da una macchina all'altra: al suo posto si conserva l'elenco delle dipendenze e l'ambiente si ricrea. Se PowerShell blocca lo script di attivazione, consentitene l'esecuzione per l'utente: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.
uv e pyproject.toml
I progetti moderni descrivono le dipendenze in pyproject.toml. Il gestore uv, usato nel corso, unisce la creazione dell'ambiente e l'installazione: uv add pandas scarica il pacchetto, lo scrive in pyproject.toml e fissa le versioni esatte in uv.lock. Il collega clona il progetto, esegue uv sync e ottiene un ambiente identico.
[project]
name = "spese"
version = "0.1.0"
requires-python = ">=3.15"
dependencies = [
"pandas>=2.3",
]
[project.scripts]
spese = "spese.cli:main"
Lazy import in Python 3.15 (PEP 810)
La novità principale della 3.15 in questo argomento è la parola chiave lazy. Le librerie pesanti si caricano a lungo: nella misura fatta nel corso sull'immagine python:3.15-slim, un normale import pandas richiedeva circa 415 ms, mentre lazy import pandas circa 13 ms. Per un'utility da riga di comando che si avvia per mostrare --help, la differenza è enorme.
lazy import pandas as pd
lazy from spese.report import build_report
def main() -> None:
...
df = pd.read_csv(path) # il vero caricamento di pandas avviene qui
Al posto del modulo il nome riceve un oggetto proxy (types.LazyImportType), e il vero import avviene solo al primo utilizzo del nome. Se non si arriva mai a quel punto, il tempo non viene speso affatto.
Limiti del lazy import
- È consentito solo al livello più alto del modulo: dentro una funzione, una classe o un
try/exceptgeneraSyntaxError. - La forma
lazy from x import *non è ammessa. ModuleNotFoundErrorcompare non all'avvio, ma al primo utilizzo.- Gli effetti collaterali del modulo (registrazione di plugin e simili) si spostano nel tempo.
Rendere pigri tutti gli import in un colpo solo si può con il flag -X lazy_imports=all o con la variabile PYTHON_LAZY_IMPORTS=all; le eccezioni si definiscono tramite sys.set_lazy_imports_filter(). Per il codice che deve funzionare anche sulle versioni vecchie c'è la variabile di modulo __lazy_modules__ = ["pandas"]: la 3.15 rende pigri gli import elencati, mentre le versioni precedenti eseguono semplicemente un normale import. Dove si perde tempo all'avvio lo mostra python -X importtime -c "import pandas". A guadagnarci sono soprattutto i programmi avviati spesso per brevi esecuzioni; un server di lunga durata ne trae quasi nulla.
Errori tipici
ModuleNotFoundError: No module named 'requests'— la libreria è stata installata in un altro Python oppure l'ambiente non è attivo. Controllate conpython -m pip show requestsusando lo stesso interprete.- Il vostro file si chiama
json.py,random.pyocsv.py— la cartella dello script è la prima insys.pathe il file copre il modulo standard. Python 3.13+ nell'errore suggerisce «consider renaming». Rinominate il file ed eliminate__pycache__. - Import circolare:
a.pyimportab.py, e questo importaa.py. Spostate il codice comune in un terzo modulo oppure importate dentro la funzione. - pip lanciato dentro l'interprete Python (
>>> pip install) — generaSyntaxError. I comandi pip si eseguono nel terminale.
Conclusioni
Il modulo è un file, il pacchetto è una cartella di moduli, la libreria è il loro insieme pronto all'uso; import cerca il modulo nella cache e in sys.path e lo esegue una sola volta. Le librerie esterne si installano con pip o uv soltanto in un ambiente virtuale venv: così i progetti non si intralciano e l'ambiente si riproduce facilmente. Nel corso di Python questo argomento è trattato in pratica: il tracker delle spese diventa un pacchetto con dipendenze e si avvia rapidamente grazie ai lazy import.
Domande frequenti
Serve creare un venv per ogni progetto?
Sì, è una buona pratica: ogni progetto ha le proprie versioni delle librerie, e un ambiente separato evita conflitti e non sporca il Python di sistema.
In che cosa pip si differenzia da uv?
pip si limita a installare pacchetti, mentre uv crea anche l'ambiente, scrive le dipendenze in pyproject.toml e fissa le versioni esatte in un file di lock; inoltre è molto più veloce.
Come vedere l'elenco delle librerie installate in Python?
Eseguite python -m pip list nell'ambiente desiderato; pip freeze mostra lo stesso elenco nel formato di requirements.txt.
Dove sono salvati i moduli installati?
Nella cartella site-packages dell'ambiente attivo; il percorso di un singolo modulo lo mostra l'attributo __file__, per esempio import requests; print(requests.__file__).
Si può copiare la cartella venv su un altro computer?
No: l'ambiente è legato ai percorsi e all'interprete della macchina. Si trasferisce l'elenco delle dipendenze e l'ambiente si crea di nuovo.
Questo argomento è trattato in modo approfondito, con pratica, nel corso «Python 3.15: от новичка до профи».
Vai al corso