Gestire i percorsi dei file in Python con <strong>pathlib</strong> e operazioni sicure

by theArchitect
SHARE
Gestire i percorsi dei file in Python con <strong>pathlib</strong> e operazioni sicure
© Guida-HTML5.it

Introduzione

Quando si lavora con i file in Python, non basta sapere come aprirli o leggerli: spesso il vero problema è costruire, controllare e usare i percorsi in modo affidabile. Un percorso file scritto “a mano” può funzionare sul tuo computer e fallire su un altro sistema operativo, oppure diventare fragile quando cambia la struttura del progetto.

In questo tutorial vedremo un sotto-argomento molto utile e pratico: come gestire i percorsi dei file e verificare l’esistenza di file e cartelle usando pathlib, con un esempio reale orientato a un piccolo archivio di documenti. Questo approccio è fondamentale in script di automazione, tool da riga di comando, applicazioni backend e progetti data-oriented.

Il vantaggio di pathlib è semplice: rende il codice più leggibile, più robusto e più portabile rispetto alla manipolazione manuale di stringhe con slash e backslash.

Codice completo

from pathlib import Path
from datetime import datetime

def prepara_archivio(base_dir: Path) -> None:
    """
    Crea la struttura minima dell´archivio:
    - base_dir/
    - base_dir/input/
    - base_dir/output/
    """
    input_dir = base_dir / "input"
    output_dir = base_dir / "output"

    input_dir.mkdir(parents=True, exist_ok=True)
    output_dir.mkdir(parents=True, exist_ok=True)

def crea_file_di_esempio(input_dir: Path) -> Path:
    """
    Crea un file di testo di esempio se non esiste già.
    Restituisce il percorso del file creato o già presente.
    """
    file_sorgente = input_dir / "documento.txt"

    if not file_sorgente.exists():
        contenuto = (
            "Titolo: Report giornalieron"
            "Data: 2026-07-02n"
            "Stato: OKn"
        )
        file_sorgente.write_text(contenuto, encoding="utf-8")

    return file_sorgente

def analizza_file(file_path: Path) -> dict:
    """
    Legge un file di testo e restituisce informazioni utili:
    - nome file
    - estensione
    - dimensione
    - data di modifica
    """
    if not file_path.exists():
        raise FileNotFoundError(f"Il file non esiste: {file_path}")

    if not file_path.is_file():
        raise ValueError(f"Il percorso non punta a un file: {file_path}")

    info = {
        "nome": file_path.name,
        "stem": file_path.stem,
        "estensione": file_path.suffix,
        "dimensione_byte": file_path.stat().st_size,
        "modificato_il": datetime.fromtimestamp(file_path.stat().st_mtime),
        "assoluto": file_path.resolve(),
    }
    return info

def copia_in_output(file_sorgente: Path, output_dir: Path) -> Path:
    """
    Copia il file sorgente nella cartella output con un nome diverso.
    """
    if not file_sorgente.exists():
        raise FileNotFoundError(f"File sorgente mancante: {file_sorgente}")

    output_dir.mkdir(parents=True, exist_ok=True)

    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    file_destinazione = output_dir / f"{file_sorgente.stem}_{timestamp}{file_sorgente.suffix}"

    contenuto = file_sorgente.read_text(encoding="utf-8")
    file_destinazione.write_text(contenuto, encoding="utf-8")

    return file_destinazione

def lista_file_testo(cartella: Path) -> list[Path]:
    """
    Elenca tutti i file .txt presenti in una cartella.
    """
    if not cartella.exists():
        raise FileNotFoundError(f"La cartella non esiste: {cartella}")

    if not cartella.is_dir():
        raise ValueError(f"Il percorso non è una cartella: {cartella}")

    return [p for p in cartella.iterdir() if p.is_file() and p.suffix.lower() == ".txt"]

def main() -> None:
    base_dir = Path("archivio_documenti")

    prepara_archivio(base_dir)

    input_dir = base_dir / "input"
    output_dir = base_dir / "output"

    file_sorgente = crea_file_di_esempio(input_dir)

    info = analizza_file(file_sorgente)
    print("Informazioni sul file:")
    for chiave, valore in info.items():
        print(f"- {chiave}: {valore}")

    file_copiato = copia_in_output(file_sorgente, output_dir)
    print(f"nFile copiato in: {file_copiato}")

    print("nFile .txt presenti nella cartella input:")
    for file_txt in lista_file_testo(input_dir):
        print(f"- {file_txt.name}")

if __name__ == "__main__":
    main()

Spiegazione

Il cuore dell’esempio è pathlib.Path, una classe pensata per rappresentare i percorsi in modo oggettuale. Invece di concatenare stringhe come "cartella" + "/" + "file.txt", usiamo l’operatore / per costruire i percorsi:

base_dir / "input" / "documento.txt"

Questo stile è più leggibile e soprattutto portabile: Python si occupa di usare il separatore corretto su Windows, Linux e macOS.

1. Creazione delle cartelle

La funzione prepara_archivio() usa:

  • mkdir(parents=True, exist_ok=True) per creare anche le cartelle intermedie;
  • exist_ok=True per evitare errori se la cartella esiste già.

Questa è una best practice importante negli script: rende il codice idempotente, cioè sicuro da rieseguire più volte.

2. Creazione di un file solo se assente

La funzione crea_file_di_esempio() controlla se documento.txt esiste già. Se non c’è, lo crea con write_text(). Questo evita di sovrascrivere accidentalmente dati già presenti.

Qui è fondamentale specificare encoding="utf-8", così il comportamento è coerente anche con caratteri accentati o simboli non ASCII.

3. Verifica del tipo di percorso

Prima di leggere informazioni sul file, analizza_file() controlla due aspetti:

  • exists(): il percorso esiste davvero?
  • is_file(): il percorso punta a un file e non a una cartella?

Questi controlli evitano errori difficili da diagnosticare. Ad esempio, chiamare stat() su un percorso inesistente genera eccezioni inutili se non si valida prima l’input.

4. Estrazione dei metadati

Il metodo stat() permette di accedere a informazioni come:

  • dimensione in byte con st_size;
  • data di modifica con st_mtime;
  • altri metadati utili per automazioni e controlli.

Nel codice convertiamo il timestamp in un oggetto datetime per renderlo leggibile.

5. Copia del file in una cartella di output

La funzione copia_in_output() mostra un caso molto comune: creare una copia del file con un nome nuovo, aggiungendo un timestamp. Questo è utile quando vuoi conservare versioni storiche o evitare collisioni di nome.

Il nuovo nome viene costruito in modo sicuro con:

  • file_sorgente.stem: nome senza estensione;
  • file_sorgente.suffix: estensione;
  • datetime.now().strftime(...): timestamp leggibile.

6. Filtrare i file per estensione

La funzione lista_file_testo() usa iterdir() per scorrere i contenuti di una cartella e selezionare solo i file con estensione .txt. Questo pattern è molto utile per elaborare batch di file, ad esempio report, esportazioni o input da pipeline automatiche.

Best practice

  • Usa sempre pathlib per i percorsi: evita concatenazioni manuali di stringhe.
  • Controlla sempre esistenza e tipo del percorso prima di leggere o scrivere.
  • Imposta esplicitamente encoding="utf-8" quando lavori con testi.
  • Preferisci mkdir(parents=True, exist_ok=True) per creare cartelle in modo robusto.
  • Non sovrascrivere file senza una logica chiara: usa timestamp, backup o controlli preventivi.
  • Usa nomi descrittivi per file e cartelle, specialmente nei progetti di automazione.
  • Separa la logica di percorso dalla logica applicativa: funzioni piccole e focalizzate sono più facili da testare.

Riepilogo

Gestire bene i percorsi dei file è una competenza essenziale in Python. Con pathlib puoi costruire percorsi in modo leggibile, verificare l’esistenza di file e cartelle, leggere metadati e organizzare operazioni come copia e filtraggio in modo sicuro.

In questo tutorial hai visto un esempio pratico con un mini-archivio di documenti, ma gli stessi concetti si applicano a moltissimi scenari reali: importazione di dati, backup, esportazioni automatiche, script di manutenzione e tool da terminale.

Se vuoi scrivere codice solido, portabile e facile da mantenere, partire da una buona gestione dei percorsi è uno dei passi più importanti.

Approfondisci con risorse ufficiali

  • Documentazione ufficiale di pathlib: https://docs.python.org/3/library/pathlib.html
  • Documentazione ufficiale di os.path per confrontare l’approccio tradizionale: https://docs.python.org/3/library/os.path.html
  • Documentazione ufficiale di datetime: https://docs.python.org/3/library/datetime.html
  • Guida alle I/O operations di Python: https://docs.python.org/3/tutorial/inputoutput.html

SHARE