Aller au contenu
Développement medium

Manipuler les chemins en Python avec pathlib (Path)

14 min de lecture

logo python

Le module pathlib représente un chemin de fichier comme un objet Path, avec des méthodes lisibles pour tester, composer, lire et parcourir l'arborescence. Fini d'empiler les appels os.path.join, os.path.exists et os.path.splitext : une seule classe Path remplace une dizaine de fonctions dispersées, avec l'opérateur / pour assembler les chemins comme on lirait une phrase. Ce guide couvre la composition de chemins, les tests (exists, is_file), la création de dossiers, la lecture-écriture de texte, le parcours avec glob et rglob, et le comparatif avec l'ancien os.path.

Il s'adresse aux développeurs qui écrivent des scripts DevOps, manipulent des fichiers de configuration ou traitent des logs, et veulent un code plus court et plus portable entre Linux, macOS et Windows. Les exemples ont été exécutés avec Python 3.12.

  • Composer un chemin avec Path et l'opérateur /.
  • Tester l'existence et la nature d'un chemin (exists, is_file, is_dir).
  • Créer des dossiers et lire-écrire du texte en une ligne.
  • Parcourir une arborescence avec iterdir, glob et rglob.
  • Extraire les composants d'un chemin (name, stem, suffix, parent).
  • Comprendre pourquoi préférer pathlib à os.path.

Tout commence par la classe Path, importée depuis le module pathlib de la bibliothèque standard. On lui passe un chemin sous forme de texte, et elle renvoie un objet chemin que l'on manipule ensuite par méthodes. Sur Linux et macOS, l'objet concret est une PosixPath ; le code, lui, reste identique quelle que soit la plateforme.

from pathlib import Path
base = Path("projet")
print(base)
print(type(base).__name__)
projet
PosixPath

L'atout central de pathlib est l'opérateur /, détourné pour assembler des chemins. Chaque / ajoute un segment, sans se soucier du séparateur de dossier propre au système d'exploitation. Le résultat se lit comme le chemin réel, ce qui rend le code immédiatement compréhensible.

config = Path("projet") / "config" / "app.toml"
print(config)
projet/config/app.toml

Deux méthodes de classe donnent des points d'ancrage courants. Path.cwd() renvoie le répertoire de travail courant, c'est-à-dire le dossier depuis lequel le script a été lancé. Path.home() renvoie le dossier personnel de l'utilisateur, l'équivalent de ~ sur un terminal. Ces deux points de départ évitent de coder en dur un chemin absolu qui casserait sur une autre machine.

from pathlib import Path
print("Répertoire courant :", Path.cwd().is_dir())
print("Dossier personnel :", Path.home().is_dir())
Répertoire courant : True
Dossier personnel : True

À partir de ces ancres, on compose vers la cible : Path.home() / ".config" / "monoutil" pointe vers le dossier de configuration de l'utilisateur, sur n'importe quel système Unix.

Avant de lire ou d'écrire, on vérifie ce qui existe vraiment. Trois méthodes couvrent l'essentiel : .exists() dit si le chemin correspond à quelque chose, .is_file() confirme qu'il s'agit d'un fichier, et .is_dir() qu'il s'agit d'un dossier. Ces tests ne lèvent jamais d'exception si le chemin est absent : ils renvoient simplement False.

from pathlib import Path
notes = Path("projet/notes.txt")
print("existe :", notes.exists())
print("est un fichier :", notes.is_file())
print("est un dossier :", notes.is_dir())
print("projet est un dossier :", Path("projet").is_dir())
existe : True
est un fichier : True
est un dossier : False
projet est un dossier : True

Ces vérifications servent de garde-fous dans un script : tester is_file() avant d'ouvrir un fichier évite un plantage brutal quand le fichier attendu n'a pas été généré.

Pour créer un dossier, .mkdir() suffit, mais ses deux options changent tout dans un script robuste. parents=True crée au passage tous les dossiers parents manquants, comme mkdir -p en shell. exist_ok=True évite l'erreur FileExistsError si le dossier est déjà là, ce qui rend le script rejouable sans planter au second lancement.

from pathlib import Path
logs = Path("projet/data/logs")
logs.mkdir(parents=True, exist_ok=True)
print("dossier créé :", logs.is_dir())
# Rejouer la même ligne ne lève aucune erreur
logs.mkdir(parents=True, exist_ok=True)
print("rejeu sans erreur : OK")
dossier créé : True
rejeu sans erreur : OK

pathlib intègre deux raccourcis qui remplacent l'ouverture manuelle d'un fichier. .write_text() écrit une chaîne complète dans le fichier, en le créant ou en l'écrasant. .read_text() renvoie tout le contenu du fichier sous forme de chaîne. Les deux gèrent l'ouverture et la fermeture du fichier pour vous. Précisez toujours encoding="utf-8" pour un comportement identique sur toutes les plateformes.

from pathlib import Path
notes = Path("projet/notes.txt")
notes.write_text("première ligne\nseconde ligne\n", encoding="utf-8")
contenu = notes.read_text(encoding="utf-8")
print(repr(contenu))
'première ligne\nseconde ligne\n'

Ces méthodes sont parfaites pour des fichiers courts : une configuration, un petit rapport, un marqueur d'état. Pour un gros fichier que l'on traite ligne par ligne, ou pour un accès en flux, l'approche classique avec open() reste préférable, détaillée dans le guide lire et écrire un fichier.

Trois méthodes explorent le contenu d'un dossier, du plus simple au plus puissant. .iterdir() liste le contenu direct d'un dossier, fichiers et sous-dossiers confondus, sans descendre plus bas.

from pathlib import Path
base = Path("projet")
for entree in sorted(base.iterdir()):
nature = "dossier" if entree.is_dir() else "fichier"
print(entree, "->", nature)
projet/config -> dossier
projet/data -> dossier
projet/notes.txt -> fichier
projet/rapport.md -> fichier

.glob() filtre par motif dans le dossier courant : *.txt ne retient que les fichiers texte. L'astérisque remplace n'importe quelle suite de caractères, mais ne traverse pas les sous-dossiers.

for fichier in sorted(base.glob("*.txt")):
print(fichier)
projet/notes.txt

.rglob() applique le même motif de façon récursive, en descendant dans toute l'arborescence. C'est l'outil idéal pour retrouver tous les fichiers d'un type donné, où qu'ils se trouvent.

for fichier in sorted(base.rglob("*.csv")):
print(fichier)
projet/data/mesures.csv

Un objet Path expose ses parties sous forme d'attributs, sans manipulation de chaîne. .name est le nom du fichier complet, .stem le nom sans extension, .suffix l'extension, .parent le dossier contenant, et .parts l'ensemble des segments sous forme de tuple.

from pathlib import Path
p = Path("projet/config/app.toml")
print("name :", p.name)
print("stem :", p.stem)
print("suffix :", p.suffix)
print("parent :", p.parent)
print("parts :", p.parts)
name : app.toml
stem : app
suffix : .toml
parent : projet/config
parts : ('projet', 'config', 'app.toml')

Ces attributs simplifient énormément le traitement par lot : renommer une série de fichiers en changeant l'extension, regrouper des logs par dossier parent, ou trier des documents par nom de base devient trivial.

Un chemin relatif comme projet/../projet/notes.txt contient des détours inutiles. .resolve() le transforme en chemin absolu normalisé : elle supprime les .., résout les liens symboliques et préfixe le répertoire courant. C'est indispensable pour comparer deux chemins de façon fiable ou pour journaliser un emplacement sans ambiguïté.

from pathlib import Path
rel = Path("projet/../projet/notes.txt")
absolu = rel.resolve()
print("nom :", absolu.name)
print("est absolu :", absolu.is_absolute())
nom : notes.txt
est absolu : True

Deux méthodes gèrent le cycle de vie d'un fichier. .rename() déplace ou renomme un chemin vers une nouvelle cible. .unlink() supprime un fichier ; son option missing_ok=True évite l'erreur si le fichier a déjà disparu, ce qui sécurise un nettoyage rejouable.

from pathlib import Path
base = Path("projet")
ancien = base / "rapport.md"
nouveau = base / "rapport-2026.md"
ancien.rename(nouveau)
print("ancien existe :", ancien.exists())
print("nouveau existe :", nouveau.exists())
temp = base / "temporaire.log"
temp.write_text("jetable", encoding="utf-8")
temp.unlink()
print("après suppression :", temp.exists())
temp.unlink(missing_ok=True) # ne lève rien, le fichier est déjà parti
print("nettoyage idempotent : OK")
ancien existe : False
nouveau existe : True
après suppression : False
nettoyage idempotent : OK

L'ancienne façon de faire reposait sur le module os.path, une collection de fonctions qui prennent des chaînes et en renvoient. Le code s'y lit de l'intérieur vers l'extérieur, en imbriquant les appels, ce qui devient vite illisible. pathlib remplace tout cela par des méthodes chaînées sur un objet, dans l'ordre naturel de lecture.

Le tableau suivant met les deux approches côte à côte sur les opérations les plus courantes. La colonne de droite se lit sans effort, et un seul import suffit là où os.path en demandait plusieurs.

Opérationos.path (ancien)pathlib (moderne)
Composeros.path.join("projet", "app.toml")Path("projet") / "app.toml"
Extensionos.path.splitext(chemin)[1]chemin.suffix
Nom du fichieros.path.basename(chemin)chemin.name
Dossier parentos.path.dirname(chemin)chemin.parent
Existeos.path.exists(chemin)chemin.exists()
Chemin absoluos.path.abspath(chemin)chemin.resolve()

Ce même traitement, exécuté dans les deux styles, produit un résultat identique. La différence n'est pas dans la sortie, mais dans la lisibilité et la maintenabilité du code.

import os.path
from pathlib import Path
chemin_os = os.path.join("projet", "config", "app.toml")
print("os.path :", os.path.basename(chemin_os), os.path.splitext(chemin_os)[1])
chemin = Path("projet") / "config" / "app.toml"
print("pathlib :", chemin.name, chemin.suffix)
os.path : app.toml .toml
pathlib : app.toml .toml

Depuis Python 3.6, la plupart des fonctions de la bibliothèque standard qui attendent un chemin acceptent directement un objet Path. Il n'y a donc aucune raison de rester sur os.path pour du code neuf : pathlib est la recommandation officielle, plus sûr entre systèmes et nettement plus agréable à relire.

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • Path représente un chemin comme un objet muni de méthodes, un seul import remplace tout os.path.
  • L'opérateur / compose les chemins dans l'ordre de lecture, sans se soucier du séparateur système.
  • Path.cwd() et Path.home() donnent des ancres portables pour construire les chemins.
  • .exists(), .is_file() et .is_dir() testent un chemin sans lever d'exception.
  • .mkdir(parents=True, exist_ok=True) crée une arborescence de façon rejouable.
  • .read_text() et .write_text() lisent et écrivent un fichier court en une ligne.
  • .iterdir(), .glob() et .rglob() parcourent un dossier, avec motif et récursion.
  • .name, .stem, .suffix, .parent et .parts décomposent un chemin sans manipuler de chaîne.
  • pathlib est la façon moderne recommandée, préférable à os.path sur tout code neuf.

pathlib gère les chemins ; ces guides couvrent le contenu des fichiers et l'automatisation qui va avec.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn