Pull to refresh
1242.7
OTUS
Цифровые навыки от ведущих экспертов

Python Path — Как использовать модуль Pathlib (с примерами)

Reading time10 min
Views42K
Original author: Rochdi Khalid

В каждой операционной системе существуют свои правила построения путей к файлам. Например, в 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.

Классы PurePath()
Классы PurePath()

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). Регистрация открыта по ссылке.

Tags:
Hubs:
Total votes 7: ↑5 and ↓2+6
Comments1

Articles

Information

Website
otus.ru
Registered
Founded
Employees
101–200 employees
Location
Россия
Representative
OTUS