Une page de Le Lab de la taverne

Hero lab enseignement Python à la .Taverne

Manipuler les chemins en Python avec pathlib

Par Darko Stankovski, 06/05/2026 — Débutant

Ajouté à la bibliothèque standard avec Python 3.4, pathlib, propose une représentation objet de la notion de chemin du système de fichiers. Chaque chemin dispose alors de méthodes et des attributs ce qui conduit à une syntaxe plus simple, intuitive et maintenable.

Dans les usages, pathlib remplace plusieurs libs comme os, os.path et glob.

Communément, l'usage de la pathlib s'articule autour de la classe Path. L'import classique est donc :

from pathlib import Path

Path est une classe abstraite qui représente un chemin de fichier ou de dossier. Python s'adapte automatiquement au système d'exploitation (Windows, macOS, Linux) et le constructeur vous retournera un objet de type concret PosixPath ou WindowsPath en fonction de votre système d'exploitation. Dans notre code, nous écrivons simplement, sans nous occuper du système d'exploitation :

p = Path('folder/subfolder/file.txt')

Le type de p dans l'exemple ci-dessus sera donc PosixPath sous Linux ou MacOs et WindowsPath sous Windows avec comme premier intérêt que le système gérera correctement les séparateurs (/ sous Linux ou MacOs, \ sous Windows).

Notez aussi que cet objet est bien créé, il représente un chemin valide même si ce chemin n'existe pas. Tout comme os.path, vous allez pouvoir travailler sur des représentation de chemin et n'accéderez au système que lorsque vous en aurez besoin : interroger le système, créer ce chemin…

Vous pouvez également créer des représentations de chemin relatifs comme :

current_path = Path('.') # Représente le répertoire courant
tests_path = Path('../tests') # Représente tests dans le répertoire parent
pictures_path = Path('~/Pictures') # Représente un chemin dans le dossier de l'utilisateur

Résolution des chemins

La pathlib propose des outils pour obtenir certains chemins usuels. Ainsi, nous avons :

path_home = Path.home() # le dossier de l'utilisateur
work_dir = Path.cwd() # le dossier de travail

Et évidemment, elle permet de résoudre les chemins relatifs que nous avons vu précédemment. Pour l'exemple, supposons que nous sommes dans le répertoire /Users/tavern/project/ et que le répertoire personnel est /Users/tavern/

>>> pictures_path.expanduser() # Retourne le chemin absolu
/Users/tavern/Pictures
>>> current_path.resolve() # Retourne aussi le chemin absolu résolvant les symlinks
/Users/tavern/project/
>>> tests_path.resolve()
/Users/tavern/tests
>>> tests_path.absolute() # Retourne le chemin absolu sans résoudre les symlinks
/Users/tavern/project/../tests

Un type immuable

Un objet Path est immuable. Vous allez voir dans la suite que nous pouvons faire plein de manipulations sur la représentation d'un chemin et à chaque fois, nous récupérerons un nouvel objet de type Path. C'était le cas ci-dessus de .resolve() ou .absolute() ce qui vous permet de réutiliser les patterns représentant un chemin plusieurs fois.

Composer des chemins

Lorsque vous devez construire un chemin à partir d'une racine, la difficulté est d'avoir un code maintenable et lisible. Surtout si il doit s'exécuter sur des systèmes différents. Historiquement, nous avions la fonction os.path.join(). Avec la pathlib, nous avons une première approche… amusante :

>>> Path.home() / 'Pictures'
/Users/tavern/Pictures

Oui, les auteurs se sont amusés à surcharger l'opérateur division décimale pour en faire un opérateur de concaténation puisque c'est le même symbole que le séparateur de segments sur les systèmes Posix.

Soyons honnêtes, cette syntaxe est parfaite pour des déclarations du fait de sa lisibilité. Mais elle est limitée pour la construction dynamique de chemins. Lorsque vous ne savez pas d'avance combien de segments vous devez ajouter, elle est impossible à utiliser. Pour ces cas là, vous avez une méthode plus adaptée :

>>> path_segments = ('project', 'tests', 'static')
>>> Path.home().joinpath(*path_segments)
/Users/tavern/project/tests/static

Accéder au système de fichiers

Une fois votre chemin construit, vous voudrez peut-être vérifier son existence. Soit p un objet de type Path, vous avez alors les classiques :

p.exists() # Test si le chemin existe
p.is_file() # Test si le chemin pointe sur un fichier
p.is_dir() # Test si le chemin pointe sur un répertoire

Leur comportement est identique aux fonctions équivalentes de os.path. pathlib propose bien d'autres méthodes de tests (symlink, point de montage…) que vous pouvez trouver dans la doc.

Contenu d'un répertoire

Il y a deux moyens d'obtenir le contenu d'un répertoire avec la pathlib qui en fait combine deux libs existantes. Tout dépends si l'on veut l'intégralité du contenu ou un filtre. Ainsi, pour p un objet de type Path représentant un répertoire, nous pouvons faire :

p.iterdir() # Permet d'obtenir le contenu du répertoire
p.glob('*.py') # Permet de filtrer le contenu du répertoire sur le pattern

La méthode Path.iterdir() nous permet donc d'avoir un itérable sur l'ensemble du contenu du répertoire et p.glob('*.py') un itérable sur les éléments du répertoire filtrés sur un pattern en utilisant les glob. Dans les deux cas, il s'agit d'une collection d'objets de type Path.

Si la méthode .glob() permet de facilement filtrer sur les noms, n'obtenir que les dossiers ou les fichiers nécessitera de passer par une compréhension de liste. Néanmoins, dans ce cas, il sera intéressant d'utiliser l'attribut .info de l'objet Path.

current_dirs = [p for p in Path('.').resolve().iterdir()
                if p.info.is_dir()]

La méthode .is_dir() sur un objet de type Path entraine un accès disque. Ce type de métadonnées est mise en cache dans l'objet .info. La documentation indique que .iterdir() collecte ces informations lors de la lecture du fichier, passer ensuite par .info.is_dir() économise un accès système tant que vous avez confiance sur la pertinence de la donnée en cache. C'est finalement une approche similaire à l'usage des DirEntry avec os.scandir().

Explorer et modifier le chemin

pathlib propose de nombreuses méthodes pour obtenir ou modifier le contenu d'un chemin et ainsi les transformer. J'ai utilisé ces possibilités dans mon tuto pour supprimer les .jpg quand on a des RAWs équivalents. Ainsi, vous avez des attributs comme :

  • p.parent : retourne un objet Path vers le répertoire parent.
  • p.name : retourne une chaine de caractères avec le nom final du chemin donc le nom du fichier (avec extension) ou répertoire.
  • p.stem : retourne une chaine de caractères représentant le nom final du chemin sans extension.
  • p.suffix : retourne une chaine de caractères représentant l'extension.

Et évidemment, vous avez les méthodes équivalentes qui permettent de modifier ces composants :

  • p.with_name('new_name.jpg') : crée un objet Path en remplaçant le nom par new_name.jpg.
  • p.with_stem('new_name') : crée un objet Path en remplaçant le stem, c'est à dire le nom en conservant l'extension
  • p.with_suffix('.jpg') : crée un objet Path en remplaçant l'extension.

N'oubliez pas là aussi que ces nouveaux chemins peuvent ne pas exister. Le code que j'ai utilisé dans le tuto sur les RAW+jpg peut s'écrire :

for file in photodir.glob('*.CR2'):
    jpg_name = file.with_suffix(".JPG")

    if jpg_name.exists():
        jpg_name.unlink()

Avec jpg_name.unlink() qui permet de supprimer un fichier. Dans la pratique, le test d'existence n'est pas nécessaire car géré par la méthode .unlink() (allez voir le tuto 😉 )

L'accès aux fichiers

pathlib est une formidable bibliothèque pour travailler avec le système de fichiers. Mais le plus souvent, on explore le système de fichiers pour trouver et ouvrir un fichier.

Un objet Path possède la méthode .open() qui ouvre un fichier exactement comme la fonction native. Il possède également des méthodes tel que .read_text() ou .write_text(data) qui permettent de lire le contenu d'un fichier ou y écrire. Mais je vois ces dernières comme accessoires.

Pour ouvrir un fichier, on a l'habitude de passer par note fonction built-in open(path). Et des milliers de lignes de code sont écrites ainsi.

La magie est que l'objet Path peut être un argument de la fonction.

my_file = Path('~/project/file.md')

with open(my_file):
    do something()

Vous n'avez donc aucun code à réécrire lors d'un passage de l'usage de chaines de caractères à la pathlib.

Cette magie s'explique par la PEP 519 qui ajoute un protocole de système de fichiers à Python depuis la version 3.6. L'objet Path possède une méthode spéciale (ou méthode magique) .__fspath__() qui retourne une chaine de caractères représentant un chemin. Ce protocole est défini par la classe abstraite os.PathLike.

Les fonctions attendant un chemin vers doivent donc, en plus d'une chaine de caractères, attendre un objet os.PathLike. et peuvent faire appel à la fonction os.fspath() pour récupérer la chaine exploitable.

En conclusion

En 2026 (date de rédaction de ce tuto), il est temps de passer à la pathlib pour manipuler les chemins. Vous avez des outils plus simples et intuitifs pour un code plus lisible. La migration se fait en douceur grâce au protocole os.PathLike avec très peu de modifications du code existant.

Licence Creative Commons

Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International