Как специалист по исследованию данных, я ежедневно работаю с путями и файлами для чтения и записи данных.

Обычно я использую модуль Python os.path для выполнения таких операций, как объединение путей, проверка содержимого каталога и создание папок. С помощью этого модуля можно с легкостью получить доступ к файловой системе.

В этой статье я опровергну эту практику, представив другую библиотеку управления путями  —  Pathlib. 

Мы посмотрим, как работает эта библиотека, чем она отличается от модуля os.path, какие возможности и преимущества она предоставляет и когда ее следует (или не следует) использовать.

Проблема модуля OS

Модуль os популярен, и он существует уже долгое время. Однако мне всегда казалось, что он обрабатывает пути неестественным образом. Вот причины, которые заставили меня усомниться в использовании этого модуля

  • ОS  —  большой модуль. В нем, конечно, есть подмодуль path для управления путями и их объединения. Однако если нужно выполнить системные операции с этими путями (создать папку, перечислить содержимое внутри нее или переименовать и удалить файл), то приходится использовать другие методы, которые либо присутствуют где-то еще в иерархии пакетов (os.makedirs, os.listdir, os.rename и т. д.), либо импортированы из других модулей, таких как shutil или glob. Хорошенько “покопавшись”, вы сможете найти эти методы, но это кажется ненужным усилием.
  • OS представляет пути в их самом грубом формат  —  строковых значениях. Это сильно ограничивает возможности, так как не дает прямого доступа к такой информации, как свойства файла и его метаданные, и не позволяет выполнять операции с файловой системой путем вызова некоторых специальных методов. 
    Например, чтобы проверить, существует ли путь, вы вводите что-то вроде os.path.exists(some_path). Но не проще ли было бы получить доступ к этой информации непосредственно из объекта path через метод или атрибут класса?
  • Модуль os не позволяет находить пути, соответствующие заданному шаблону, внутри иерархии. Допустим, нужно рекурсивно найти все файлы __init__.py внутри сложной структуры папок. Чтобы сделать это, придется объединить os с другим модулем под названием glob. К этому, конечно, можно привыкнуть, но неужели вам нужно два модуля для выполнения такой задачи?
  • Это скорее вопрос личного предпочтения, но я всегда считал синтаксис os немного громоздким.

Что такое Pathlib?

Pathlib является частью стандартной библиотеки Python. Она была введена в версию Python 3.4 (см. PEP 428) с целью представления путей не в виде простых строк, а в виде многофункциональных объектов Python с множеством полезных методов и атрибутов.

Вот определение из официальной документации:

“Цель этой библиотеки  —  предоставить простую иерархию классов для работы с путями файловой системы и обычными операциями, которые пользователи выполняют с ними”.

Pathlib призвана облегчить вышеупомянутые трудности, возникающие при использовании модуля os. Давайте посмотрим на некоторые из возможностей этого компонента Python.

Более подробную информацию об этой библиотеке можно найти в официальной документации.

У Pathlib более интуитивно понятный синтаксис

Чтобы создать путь с помощью Pathlib, нужно импортировать класс Path и передать ему строку. Эта строка указывает на путь в файловой системе, который не обязательно должен существовать.

from pathlib import Path

path = Path("/Users/ahmed.besbes/projects/posts")

path

# PosixPath('/Users/ahmed.besbes/projects/posts')

print(cwd)

# /Users/ahmed.besbes/projects/posts

Теперь у вас есть доступ к объекту Path. Как выполнять простые операции?

  • Объединение путей

Pathlib использует оператор / для объединения путей. Сначала эта функция может показаться забавной, но на самом деле она облегчает чтение кода.

Давайте сравним. Чтобы объединить пути с помощью модуля os, нужно сделать примерно следующее:

import os

in_file = os.path.join(os.getcwd(), "raw_data", "input.xlsx")
out_file = os.path.join(os.getcwd(), "processed_data", "output.xlsx")

С Pathlib тот же код будет выглядеть вот так:

from pathlib import Path

in_file = Path.cwd() / "raw_data" / "input.xlsx"
out_file = Path.cwd() / "processed_data" / "output.xlsx"

По сути, Pathlib усовершенствовал оператор / для выполнения объединения путей.

  • Получение текущего рабочего/ домашнего каталога

Под эту задачу уже предусмотрены методы:

from path import Pathlib

cwd = Path.cwd()
home = Path.home()
  • Чтение файла

Вы можете либо использовать open с контекстным менеджером, как это делается в случае обычного пути, либо использовать read_text или read_bytes.

>>> path = pathlib.Path.home() / file.txt'
>>> path.read_text()

Очевидно, что функций у Pathlib гораздо больше. Рассмотрим наиболее интересные из них.

Простое создание файлов и каталогов

После того, как объект Path создан, он может самостоятельно выполнять операции с файловой системой, вызывая свои внутренние методы. Например, объект может создать папку или открыть файл, просто вызвав методы mkdir и touch.

Вот как объект Path создает папку:

from pathlib import Path

random_folder = Path("random_folder")

random_folder.exists()
# False

random_folder.mkdir(exist_ok=True)

random_folder.exists()
# True

Тот же принцип используется и при создании файлов:

from pathlib import Path

random_file = Path("random_file.txt")

random_file.exists()
# False

random_file.touch()

random_file.exists()
# True

Конечно, вы можете выполнить эти операции с помощью модуля os, но для этого необходимо вызвать другую функцию, например makedirs:

import os

random_folder = os.path.join("random_folder")

os.makedirs(random_folder, exist_ok=True)

Перемещение по иерархии файловой системы с помощью обращения к родителям

Каждый объект Path имеет свойство parent, которое возвращает объект Path родительской папки. Это облегчает работу с большими иерархиями папок. Поскольку Path являются объектами, для доступа к нужному родителю можно использовать цепочку методов:

from pathlib import Path

path = Path("/Users/ahmed.besbes/Downloads/")

path
# PosixPath('/Users/ahmed.besbes/Downloads')

path.parent
# PosixPath('/Users/ahmed.besbes')

path.parent.parent
# PosixPath('/Users')

path.parent.parent.parent
# PosixPath('/')

Чтобы избежать построения цепочки свойств parent для доступа к n-му предыдущему родителю, можно вызвать свойство parents, которое возвращает список всех родителей, предшествующих текущей папке:

import pathlib

path = Path("/Users/ahmed.besbes/Downloads/")

list(path.parents)
# [PosixPath('/Users/ahmed.besbes'), PosixPath('/Users'), PosixPath('/')]

Выполнение итераций в каталогах и подгонка под шаблон

Предположим, у вас есть объект Path, который указывает на каталог. Pathlib позволяет легко итерировать содержимое этого каталога, а также получать файлы и папки, соответствующие определенному шаблону.

Помните модуль glob, который импортировался вместе с модулем os, чтобы получить пути, соответствующие шаблону?

Так вот, объекты Path включают метод glob и его рекурсивную версию rglob для выполнения аналогичных задач, но с гораздо более легким синтаксисом.

Допустим, нужно подсчитать количество файлов Python в конкретной папке. Вот как это можно сделать:

from pathlib import Path

# Действительно, довольно большая папка!
path = Path("/Users/ahmed.besbes/anaconda3/")

python_files = path.rglob("**/*.py")

next(python_files)
# PosixPath('/Users/ahmed.besbes/anaconda3/bin/rst2xetex.py')

next(python_files)
# PosixPath('/Users/ahmed.besbes/anaconda3/bin/rst2latex.py')

next(python_files)
# PosixPath('/Users/ahmed.besbes/anaconda3/bin/rst2odt_prepstyles.py')

...

len(list(python_files))
# 67481

Каждый объект Path имеет множество полезных атрибутов

Каждый объект Path имеет множество полезных методов и атрибутов, которые совершают операции, ранее выполнявшиеся другими библиотеками, а не os (например, glob и shutil).

  • .exists(): проверка наличия пути в файловой системе.
  • .is_dir(): проверка соответствия пути каталогу.
  • .is_file(): проверка соответствия пути файлу.
  • .is_absolute(): проверка абсолютности пути.
  • .chmod(): изменение режима файла и разрешений.
  • is_mount() : проверка того, является ли путь точкой монтирования.
  • .suffix : получение расширение файла.

Это еще не все методы. Вы можете ознакомиться с полным их перечнем здесь.


Это была краткая статья о некоторых возможностях Pathlib. Если вы используете версию Python +3.4 и думаете о переходе на Pathlib, смело делайте это. Переход с os на Pathlib довольно прост.

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Ahmed Besbes: Why You Should Start Using Pathlib as an Alternative to the OS Module

Предыдущая статьяРуководство по Docker. Часть 1: образ, контейнер, сопоставление портов и основные команды
Следующая статьяЧистая архитектура: руководство для начинающих