Python: `pathlib` al posto di `os.path`, con le differenze che contano
pathlib tratta i percorsi come oggetti invece che come stringhe. Il codice è più leggibile; in cambio costa un po' di tempo, e conviene sapere dove.
Le equivalenze che usi ogni giorno
import os
from pathlib import Path
p = Path("/dati") / "sub" / "a.txt"
os.path.join("/dati", "sub", "a.txt") # p
os.path.basename(p) # p.name -> "a.txt"
os.path.dirname(p) # p.parent
os.path.splitext(p)[1] # p.suffix -> ".txt"
p.stem # "a"
p.with_suffix(".md") # /dati/sub/a.md
p.relative_to("/dati") # sub/a.txt
p.relative_to("/dati").parts # ('sub', 'a.txt')
Ho verificato con assert che i risultati coincidono con quelli di os.path.
Leggere, scrivere, cercare
q = Path("n.txt")
q.write_text("è", encoding="utf-8")
q.read_text(encoding="utf-8") # 'è'
list(Path(".").glob("*.txt")) # solo la cartella
list(Path(".").rglob("*.txt")) # anche le sottocartelle
Indica sempre encoding: senza, Python usa quello del sistema, che cambia da una macchina all'altra. Path.glob("*.txt") restituisce gli stessi file di glob.glob.
La trappola: / con un percorso assoluto
Path("/a") / "/etc" / "x" # PosixPath('/etc/x')
Se un pezzo è assoluto, scarta tutto quello che veniva prima. Succede davvero quando il pezzo viene da input dell'utente (Path(base) / nome_file): un nome che inizia con / esce dalla cartella base. os.path.join si comporta allo stesso modo. Se il nome arriva dall'esterno, risolvi e controlla:
dest = (base / nome).resolve()
if not dest.is_relative_to(base.resolve()): # Python 3.9+
raise ValueError("percorso fuori dalla cartella")
Velocità
Misure (Python 3.12.15, macOS, migliore di 5 serie; cartella con 601 file, 301 .txt):
| Operazione | Tempo |
|---|---|
os.path.join(...) |
~1,4 µs |
Path / "sub" / "a.txt" |
~4,0 µs |
glob.glob |
~1,1 ms |
Path.glob |
~1,4 ms |
os.listdir + splitext |
~1,2 ms |
os.scandir |
~0,7 ms |
iterdir() + .suffix |
~1,9 ms |
Path è circa 3 volte più lento a costruire percorsi e un po' più lento sulle liste. Sono microsecondi: per una manciata di file non cambia nulla. Se scorri centinaia di migliaia di file in un ciclo caldo, os.scandir è la scelta più veloce.
Quando usare cosa
- Codice normale, script, configurazioni:
pathlib. Meno import e menojoinannidati. - Ciclo caldo su molti file:
os.scandir. - Librerie che chiedono stringhe: la maggior parte accetta già
Path; altrimentistr(p).
Lo script che ha prodotto le misure è verifica/pathlib_confronto.py nel sorgente del sito. I tempi dipendono dalla macchina e dal disco; non li ho provati su Windows.