В каждой операционной системе существуют свои правила построения путей к файлам. Например, в Linux для путей используются прямые слэши (“/”), а в Windows — обратные слэши (“\”).
Это незначительное отличие может создать проблемы, если вы занимаетесь проектом и хотите, чтобы другие разработчики, работающие в разных операционных системах, могли дополнить ваш код.
К счастью, если вы пишете на Python, то с этой задачей успешно справляется модуль Pathlib. Он обеспечит одинаковую работу ваших путей к файлам в разных операционных системах. Кроме того, он предоставляет функциональные возможности и операции, которые помогут вам сэкономить время при обработке и манипулировании путями.
Необходимые условия
Pathlib по умолчанию поставляется с Python >= 3.4. Однако, если вы используете версию Python ниже 3.4, у вас не будет доступа к этому модулю.
Как работает Pathlib?
Чтобы разобраться, как можно построить базовый путь с помощью Pathlib, давайте создадим новый файл Python example.py и поместим его в определенный каталог.
Откройте файл и введите следующее:
import pathlib p = pathlib.Path(__file__) print(p)
example.py
В данном примере мы импортируем модуль Pathlib. Затем создаем новую переменную p, чтобы сохранить путь. Здесь мы используем объект Path из Pathlib со встроенной в Python переменной под названием __file__. Эта переменная служит ссылкой на путь к файлу, в котором мы ее прописываем, а именно, example.py.
Если мы выведем p, то получим путь к файлу, в котором сейчас находимся:
/home/rochdikhalid/dev/src/package/example.py
Выше показано, что Pathlib создает путь к этому файлу, помещая этот конкретный скрипт в объект Path. Pathlib содержит множество объектов, таких как PosixPath() и PurePath(), о которых мы узнаем больше в следующих разделах.
Pathlib делит пути файловой системы на два разных класса, которые представляют два типа объектов пути: Pure Path и Concrete Path.

Pure path предоставляет утилиты для обработки пути к файлу и манипулирования им без совершения операций записи, в то время как Concrete path позволяет производить обработку пути к файлу и выполнять операции записи.
Другими словами, Concrete path — это подкласс Pure path. Он наследует манипуляции от родительского класса и добавляет операции ввода/вывода, которые выполняют системные вызовы.
Pure path в Python
Pure path управляет путем к файлу на вашей машине, даже если он принадлежит другой операционной системе.
Допустим, вы работаете в Linux и хотите использовать путь к файлу Windows. Здесь объекты класса Pure path помогут вам обеспечить работу пути на вашей машине с некоторыми базовыми операциями, такими как создание дочерних путей или доступ к отдельным частям пути.
Но Pure path не сможет имитировать некоторые другие операции, такие как создание каталога или файла, потому что вы не находитесь в этой операционной системе.
Как использовать Pure path
Как видно из приведенной выше диаграммы, Pure path состоит из трех классов, которые обрабатывают любой путь к файловой системе на вашем компьютере:
PurePath() — это корневой узел, который обеспечивает операции по обработке каждого объекта пути в Pathlib.
Когда вы инстанцируете PurePath(), он создает два класса для обработки путей Windows и других, отличных от Windows. PurePath() создает общий объект пути "agnostic path", независимо от операционной системы, в которой вы работаете.
In [*]: pathlib.PurePath('setup.py') Out[*]: PurePosixPath('setup.py')
PurePath() в приведенном выше примере создает PurePosixPath(), поскольку мы предположили, что работаем на машине Linux. Но если вы создадите его на Windows, то получите что-то вроде PureWindowsPath('setup.py').
PurePosixPath() — это дочерний узел PurePath(), реализованный для путей файловой системы, отличной от Windows.
In [*]: pathlib.PurePosixPath('setup.py') Out[*]: PurePosixPath('setup.py')
Если вы инстанцируете PurePosixPath() в Windows, то не возникнет никакой ошибки, просто потому что этот класс не выполняет системных вызовов.
PureWindowsPath() — это дочерний узел PurePath(), реализованный для путей файловой системы Windows.
In [*]: pathlib.PureWindowsPath('setup.py') Out[*]: PureWindowsPath('setup.py')
То же самое относится и к PureWindowsPath(), поскольку этот класс не предусматривает системных вызовов, следовательно, его инстанцирование не вызовет ошибок для других операционных систем.
Свойства Pure path
Каждый подкласс в PurePath() предоставляет следующие свойства:
PurePath().parent выводит родительский класс:
In [*]: pathlib.PurePath('/src/goo/scripts/main.py').parent Out[*]: PurePosixPath('/src/goo/scripts')
В примере выше мы используем свойство .parent, чтобы получить путь к логическому родителю main.py.
PurePath().parents[] выводит предков пути:
In [*]: p = pathlib.PurePath('/src/goo/scripts/main.py') p.parents[0] Out[*]: PurePosixPath('/src/goo/scripts') In [*]: p.parents[1] Out[*]: PurePosixPath('/src/goo')
Вы всегда должны указывать индекс предка в квадратных скобках, как показано выше. В Python 3.10 и выше можно использовать срезы и отрицательные значения индекса.
PurePath().name предоставляет имя последнего компонента вашего пути:
In [*]: pathlib.PurePath('/src/goo/scripts/main.py').name Out[*]: 'main.py'
В этом примере конечным компонентом пути является main.py. Таким образом, свойство .name выводит имя файла main.py, то есть main с суффиксом .py.
В свою очередь, PurePath().suffix предоставляет расширение файла последнего компонента вашего пути:
In [*]: pathlib.PurePath('/src/goo/scripts/main.py').suffix Out[*]: '.py'
По сравнению со свойством .name, .suffix выводит расширение файла и исключает имя файла.
PurePath().stem выводит только имя конечного компонента вашего пути без суффикса:
In [*]: pathlib.PurePath('/src/goo/scripts/main.py').stem Out[*]: 'main'
Как видно выше, свойство .stem исключает суффикс конечного компонента main.py и предоставляет только имя файла.
Методы Pure path
Каждый подкласс PurePath() предоставляет следующие методы:
PurePath().is_absolute() проверяет, является ли ваш путь абсолютным или нет:
In [*]: p = pathlib.PurePath('/src/goo/scripts/main.py') p.is_absolute() Out[*]: True In [*]: o = pathlib.PurePath('scripts/main.py') o.is_absolute() Out[*]: False
Обратите внимание, что абсолютный путь состоит из корня и имени диска. В данном случае PurePath() не позволяет нам узнать имя диска.
Если вы используете PureWindowsPath(), то можете репрезентовать абсолютный путь, содержащий имя диска, как PureWindowsPath('c:/Program Files').
PurePath().is_relative() проверяет, принадлежит ли данный путь другому заданному пути или нет:
In [*]: p = pathlib.PurePath('/src/goo/scripts/main.py') p.is_relative_to('/src') Out[*]: True In [*]: p.is_relative_to('/data') Out[*]: False
В данном примере заданный путь /src является частью или принадлежит пути p, в то время как другой заданный путь /data выдает False, поскольку он не имеет никакого отношения к пути p.
PurePath().joinpath() конкатенирует путь с заданными аргументами (дочерними путями):
In [*]: p = pathlib.PurePath('/src/goo') p.joinpath('scripts', 'main.py') Out[*]: PurePosixPath('/src/goo/scripts/main.py')
Обратите внимание, что нет необходимости добавлять слэши в заданные аргументы, так как метод .joinpath() делает это за вас.
PurePath().match() проверяет, соответствует ли путь заданному шаблону:
In [*]: pathlib.PurePath('/src/goo/scripts/main.py').match('*.py') Out[*]: True In [*]: pathlib.PurePath('/src/goo/scripts/main.py').match('goo/*.py') Out[*]: True In [*]: pathlib.PurePath('src/goo/scripts/main.py').match('/*.py') Out[*]: False
Исходя из приведенных примеров, шаблон должен совпадать с путем. Если заданный шаблон является абсолютным, то и путь должен быть абсолютным.
PurePath().with_name() изменяет имя конечного компонента вместе с его суффиксом:
In [*]: p = pathlib.PurePath('/src/goo/scripts/main.py') p.with_name('app.js') Out[*]: PurePosixPath('/src/goo/scripts/app.js') In [*]: p Out[*]: PurePosixPath('/src/goo/scripts/main.py')
Метод .with_name() не изменяет имя последнего компонента навсегда. Кроме того, если указанный путь не содержит имени, возникает ошибка, как отмечено в официальной документации.
PurePath().with_stem() изменяет только имя последнего компонента пути:
In [*]: p = pathlib.PurePath('/src/goo/scripts/main.py') p.with_stem('app.py') Out[*]: PurePosixPath('/src/goo/scripts/app.py') In [*]: p Out[*]: PurePosixPath('/src/goo/scripts/main.py')
Это аналогично методу .with_name(). Метод .with_stem() изменяет имя последнего компонента на время. Также, если указанный путь не содержит имени, произойдет ошибка.
PurePath().with_suffix() временно изменяет суффикс или расширение последнего компонента пути:
In [*]: p = pathlib.PurePath('/src/goo/scripts/main.py') p.with_suffix('.js') Out[*]: PurePosixPath('/src/goo/scripts/main.js')
Если имя заданного пути не содержит суффикса, метод .with_suffix() добавляет суффикс за вас:
In [*]: p = pathlib.PurePath('/src/goo/scripts/main') p.with_suffix('.py') Out[*]: PurePosixPath('/src/goo/scripts/main.py')
Но если мы не включим суффикс и оставим аргумент пустым ' ', текущий суффикс будет удален.
In [*]: p = pathlib.PurePath('/src/goo/scripts/main') p.with_suffix('') Out[*]: PurePosixPath('/src/goo/scripts/main')
Некоторые методы, такие как .with_stem() и .is_relative_to(), были недавно добавлены в Python 3.9 и выше. Поэтому, если вы их вызываете, используя Python 3.8 или ниже, будет выдана ошибка атрибута.
Concrete Path в Python
Concrete Paths позволяет обрабатывать, манипулировать и выполнять операции записи над различными путями файловой системы.
Другими словами, этот тип объекта пути помогает нам создать, скажем, новый файл, новый каталог и выполнить другие операции ввода/вывода, не находясь в данной операционной системе.
Как использовать Concrete path
В Concrete path обрабатываются любые пути файловой системы и выполняются системные вызовы на вашем компьютере. Эти объекты пути являются дочерними путями Pure path и состоят из трех подклассов, как и сами Pure path:
Path() — это дочерний узел PurePath(), он обеспечивает операции обработки с возможностью выполнения процесса записи.
Когда вы инстанцируете Path(), он создает два класса для работы с путями Windows и отличных от Windows. Как и PurePath(), Path() также создает общий объект пути "agnostic path", независимо от операционной системы, в которой вы работаете.
In [*]: pathlib.Path('setup.py') Out[*]: PosixPath('setup.py')
Path() в приведенном выше примере создает PosixPath(), поскольку мы предполагаем, что работаем на Linux. Но если вы создадите его в Windows, то получите что-то вроде WindowsPath('setup.py').
PosixPath() — это дочерний узел Path() и PurePosixPath(), реализованный для обработки и управления путями файловой системы, отличной от Windows.
In [*]: pathlib.PosixPath('setup.py') Out[*]: PosixPath('setup.py')
Вы получите ошибку при инстанцировании PosixPath() на компьютере с Windows, поскольку нельзя выполнять системные вызовы во время работы в другой операционной системе.
WindowsPath() — это дочерний узел Path() и PureWindowsPath(), реализованный для путей файловой системы Windows.
In [*]: pathlib.WindowsPath('setup.py') Out[*]: WindowsPath('setup.py')
То же самое относится и к WindowsPath(), поскольку вы работаете в другой операционной системе — поэтому ее инстанцирование приведет к ошибке.
Свойства Concrete path
Поскольку Concrete path является подклассом Pure path, вы можете делать с Concrete path все, что угодно, используя свойства PurePath(). Это означает, что мы можем использовать, например, свойство .with_suffix для добавления суффикса к Concrete path:
In [*]: p = pathlib.Path('/src/goo/scripts/main') p.with_suffix('.py') Out[*]: PosixPath('/src/goo/scripts/main.py')
Или вы можете проверить, относится ли данный путь к исходному пути с помощью .is_relative_to:
In [*]: p = pathlib.Path('/src/goo/scripts/main.py') p.is_relative_to('/src') Out[*]: True
Всегда помните, что Concrete path наследуют операции обработки от Pure path и добавляют операции записи, которые выполняют системные вызовы и конфигурации ввода/вывода.
Методы Concrete path
Каждый подкласс Path() предоставляет следующие методы для обработки путей и выполнения системных вызовов:
Path().iterdir() возвращает содержимое каталога. Допустим, у нас есть следующая папка, содержащая следующие файлы:
data population.json density.json temperature.yml stats.md details.txt
папка данных
Чтобы вернуть содержимое каталога /data, вы можете использовать метод .iterdir():
In [*]: p = pathlib.Path('/data') for child in p.iterdir(): print(child) Out[*]: PosixPath('/data/population.json') PosixPath('/data/density.json') PosixPath('/data/temprature.yml') PosixPath('/data/stats.md') PosixPath('/data/details.txt')
Метод .itertir() создает итератор, который случайным образом перечисляет файлы.
Path().exists() проверяет, существует ли файл/каталог в текущем пути. Давайте воспользуемся каталогом из предыдущего примера (наш текущий каталог — /data):
In [*]: p = pathlib.Path('density.json').exists() p Out[*]: True
Метод .exists() возвращает True, если заданный файл существует в каталоге data. Метод возвращает False, если его нет.
In [*]: p = pathlib.Path('aliens.py').exists() p Out[*]: False
То же самое относится и к каталогам, метод возвращает True, если заданный каталог существует, и False, если каталог отсутствует.
Path().mkdir() создает новый каталог по заданному пути:
In [*]: p = pathlib.Path('data') directory = pathlib.Path('data/secrets') directory.exists() Out[*]: False In [*]: directory.mkdir(parents = False, exist_ok = False) directory.exists() Out[*]: True
Согласно официальной документации, метод .mkdir() принимает три аргумента. В данный момент мы сосредоточимся только на аргументах parents и exist_ok.
По умолчанию оба аргумента имеют значение False. Аргумент parents выдает ошибку FileNotFound в случае отсутствия родителя, а exist_ok выдает ошибку FileExists, если данный каталог уже существует.
В приведенном примере вы можете установить аргументы в True, чтобы игнорировать упомянутые ошибки и обновить каталог.
Мы также можем создать новый файл по указанному пути с помощью метода Path().touch():
In [*]: file = pathlib.Path('data/secrets/secret_one.md') file.exists() Out[*]: False In [*]: file.touch(exist_ok = False) file.exists() Out[*]: True
Та же логика применима к методу .touch(). Здесь параметр exist_ok может быть установлен в True, чтобы игнорировать ошибку FileExists и обновить файл.
Path().rename() переименовывает файл/каталог по заданному пути. Рассмотрим пример на примере нашего каталога /data:
In [*]: p = pathlib.Path('density.json') n = pathlib.Path('density_2100.json') p.rename(n) Out[*]: PosixPath('density_2100.json')
Если вы присваиваете методу несуществующий файл, он выдает ошибку FileNotFound. То же самое относится и к каталогам.
Path().read_text() возвращает содержимое файла в формате строки:
In [*]: p = pathlib.Path('info.txt') p.read_text() Out[*]: 'some text added'
Также вы можете использовать метод write_text() для записи содержимого в файл:
In [*]: p = pathlib.Path('file.txt') p.write_text('we are building an empire') Out[*]: 'we are building an empire'
Обратите внимание, что метод .write_text() был добавлен в Python 3.5 и недавно был обновлен в Python 3.10, получив некоторые дополнительные параметры.
Важное замечание
Вы можете спросить себя, зачем использовать пути файловой системы Windows — ведь каждый пакет должен быть совместим и с другими операционными системами, а не только с Windows.
Вы правы, если цель состоит в том, чтобы сделать путь, не зависящий от ОС. Но иногда мы не можем этого сделать из-за некоторых параметров, уникальных для Windows или Posix систем. Вот почему предоставляются данные объекты — чтобы помочь разработчикам справиться с такими вариантами использования.
Некоторые пакеты нацелены на решение проблем, присутствующих только в экосистеме Windows, и Python поддерживает эти юзкейсы в данной библиотеке.
Что дальше?
Надеемся, это руководство помогло вам узнать, как и зачем использовать Pathlib и в чем его польза для обработки и манипулирования путями файловой системы.
Было бы здорово обыграть полученные знания и воплотить их в реальном проекте.
В этой статье я рассказал об основах, необходимых для использования Pathlib в вашем проекте.
В официальной документации описано больше методов и свойств, которые вы можете применить к путям файловой системы: Pathlib - Объектно-ориентированные пути файловой системы
Всех желающих приглашаем на открытое занятие, на котором научимся работать со встроенными модулями. Узнаем про модули (os, pathlib, functools). Регистрация открыта по ссылке.
