Как использовать модуль Pathlib

В этой статье мы на примерах разберем, как использовать модуль Pathlib в Python. Все операционные системы имеют разные правила построения путей к файлам. Например, в Linux для путей используется косая черта (слэш, /), а в Windows — обратная косая черта (обратный слэш, \).

Эта небольшая разница может вызвать ряд проблем, если вы работаете над проектом и хотите, чтобы разработчики могли работать с вашим кодом и в других операционных системах.

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

Установка

Pathlib поставляется по умолчанию с Python (версии 3.4 и выше). Однако если вы используете версию Python ниже 3.4, у вас не будет доступа к этому модулю.

Как работает Pathlib?

Чтобы понять, как создается базовый путь с помощью Pathlib, давайте рассмотрим пример. Создадим новый файл Python с именем example.py и поместим его в определенный каталог.

Откройте файл и введите следующее содержимое:

import pathlib

p = pathlib.Path(__file__)
print(p)

В этом примере мы импортируем модуль 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. Он наследует манипуляции от родительского класса и добавляет операции ввода/вывода, которые выполняют системные вызовы.

[python_ad_block]

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() дела обстроят так же, как и с PurePosixPath(): поскольку этот класс не выполняет системных вызовов, его создание не вызовет ошибок для других операционных систем.

Свойства PurePath

Каждый подкласс в PurePath() предоставляет следующие свойства:

1. PurePath().parent выводит родительский класс:

In [*]: pathlib.PurePath('/src/goo/scripts/main.py').parent                     
Out[*]: PurePosixPath('/src/goo/scripts')

В приведенном выше примере мы используем свойство .parent, чтобы получить путь к логическому родителю main.py.

2. 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 и выше вы также можете использовать срезы и отрицательные значения индекса.

3. PurePath().name предоставляет имя последнего компонента вашего пути:

In [*]: pathlib.PurePath('/src/goo/scripts/main.py').name                      
Out[*]: 'main.py'

В этом примере последний компонент пути — main.py. Таким образом, свойство .name выводит имя файла main.py.

4. А PurePath().suffix предоставляет расширение файла последнего компонента вашего пути:

In [*]: pathlib.PurePath('/src/goo/scripts/main.py').suffix                    
Out[*]: '.py'

Свойство .suffix, в отличие от .name, выводит только расширение файла и исключает его имя.

5. PurePath().stem выводит, наоборот, только имя конечного компонента вашего пути без суффикса:

In [*]: pathlib.PurePath('/src/goo/scripts/main.py').stem                      
Out[*]: 'main'

Как видно выше, свойство .stem исключает суффикс конечного компонента main.py и предоставляет только имя файла.

Методы PurePath

Кроме того, каждый подкласс PurePath() предоставляет следующие методы:

1. 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').

2. 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.

3. PurePath().joinpath() объединяет путь с заданными аргументами (дочерние пути):

In [*]: p = pathlib.PurePath('/src/goo')
        p.joinpath('scripts', 'main.py')

Out[*]: PurePosixPath('/src/goo/scripts/main.py')

Обратите внимание, что нет необходимости добавлять слэши в указанные вами аргументы, так как метод .joinpath() сделает это за вас.

4. 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

Исходя из приведенных выше примеров, шаблон должен соответствовать пути. Если данный шаблон является абсолютным, путь также должен быть абсолютным.

5. 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() не изменяет имя последнего компонента навсегда. Кроме того, если указанный путь не содержит имени, возникает ошибка, как указано в официальной документации.

6. 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() временно изменяет лишь имя последнего компонента. Кроме того, если указанный путь не содержит имени, произойдет ошибка.

7. 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.py')
        p.with_suffix('')
Out[*]: PurePosixPath('/src/goo/scripts/main')

Некоторые методы, такие как .with_stem() и .is_relative_to(), недавно были добавлены в Python 3.9 и выше. Поэтому, если вы вызовете эти методы с помощью Python 3.8 или ниже, возникнет ошибка.

Concrete Paths в Python

Concrete Paths позволяет обрабатывать, манипулировать и выполнять операции записи над различными путями файловой системы.

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

Как использовать Concrete Paths

Concrete Paths обрабатывают любой путь к файловой системе и выполняют системные вызовы на вашем компьютере. Эти объекты пути являются дочерними путями PurePath и состоят из трех подклассов, как и PurePath:

1. 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')

2. PosixPath() — это дочерний класс Path() и PurePosixPath(), реализованный для обработки и управления путями файловой системы, отличной от Windows.

In [*]: pathlib.PosixPath('setup.py')                                            
Out[*]: PosixPath('setup.py')

Вы получите сообщение об ошибке, если создадите экземпляр PosixPath() на компьютере с Windows, потому что нельзя выполнять системные вызовы, работая в другой операционной системе.

3. WindowsPath() — это дочерний класс Path() и PureWindowsPath(), реализованный для путей файловой системы Windows.

In [*]: pathlib.WindowsPath('setup.py')                                     
Out[*]: WindowsPath('setup.py')

Создание WindowsPath(), если вы работаете в другой операционной системе, тоже вызовет ошибку.

Свойства Concrete Paths

Поскольку Concrete Paths является подклассом PurePath, мы можем использовать все свойства PurePath(). Это означает, что мы можем использовать, например, свойство .with_suffix для добавления суффикса к конкретному пути:

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 Paths наследуют операции обработки от PurePath и добавляют операции записи, которые выполняют системные вызовы и конфигурации ввода/вывода.

Методы Concrete Paths

Каждый подкласс Path() предоставляет следующие методы для обработки путей и выполнения системных вызовов:

1. Path().itertir() возвращает содержимое каталога. Допустим, у нас есть папка, содержащая следующие файлы:

data
	population.json
	density.json
	temperature.yml
	stats.md
	details.txt

Чтобы вернуть содержимое каталога /data, вы можете использовать метод .itertir():

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() создает итератор, который случайным образом перечисляет файлы.

2. 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, если его нет.

3. 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 и exists_ok.

Оба аргумента имеют значение False по умолчанию. Аргумент parents вызывает ошибку FileNotFound в случае отсутствия родителя, тогда как exists_ok вызывает ошибку FileExists, если данный каталог уже существует.

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

4. Мы также можем создать новый файл по указанному пути, используя метод 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

Здесь для exists_ok тоже можно установить значение True, чтобы игнорировать ошибку FileExists и обновлять файл.

5. Path().rename() переименовывает файл/каталог по указанному пути. Давайте рассмотрим пример, используя наш каталог /data:

In [*]: p = pathlib.Path('density.json')
        n = pathlib.Path('density_2100.json')
        p.rename(n)
Out[*]: PosixPath('density_2100.json')

Если вы передадите методу несуществующий файл, он вызовет ошибку FileNotFound. То же самое относится и к каталогам.

6. 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 или Posix. Некоторые пакеты нацелены на решение проблем, присутствующих только в экосистеме Windows, и Python поддерживает эти юзкейсы в данной библиотеке.

Заключение

Итак, мы познакомились с модулем Pathlib в Python и на примерах разобрали, как его использовать Надеемся, вы поняли, насколько он полезен для обработки и управления путями файловой системы.

В официальной документации вы найдете больше методов и свойств, которые сможете применить к путям вашей файловой системы.

Перевод статьи «Python Path – How to Use the Pathlib Module with Examples».