Введение
В этой лабораторной работе вы научитесь работать с путями к файлам и каталогам в Python, используя современный модуль pathlib. Хотя старый модуль os.path все еще широко распространен, pathlib предлагает объектно-ориентированный подход, который делает манипуляции с путями более интуитивно понятными и читаемыми. Вы научитесь создавать, проверять, валидировать и изменять пути, файлы и каталоги таким образом, чтобы это работало на разных операционных системах, таких как Windows, macOS и Linux.
К концу этой лабораторной работы вы будете уверенно владеть следующими навыками:
- Правильное объединение компонентов пути.
- Создание и управление каталогами.
- Различение абсолютных и относительных путей.
- Проверка существования и типа путей.
- Выполнение общих файловых операций, таких как копирование, перемещение и удаление.
- Обход древовидных структур каталогов для поиска файлов и папок.
Базовые операции с путями
На этом этапе вы изучите основы манипулирования путями с использованием модуля pathlib. Это включает объединение компонентов пути, получение текущего рабочего каталога и создание новых каталогов.
Модуль pathlib предоставляет элегантный, кроссплатформенный способ работы с путями файловой системы. В Windows пути используют обратные слеши (\), тогда как в Unix-подобных системах (Linux, macOS) используются прямые слеши (/). Использование pathlib гарантирует, что ваш код будет работать на любой платформе без ручной настройки.
Примечание: Если вы не знакомы с использованием WebIDE (похожего на VS Code), пожалуйста, изучите его сначала в этой лабораторной работе.
Сначала посмотрим, как объединять компоненты пути. Оператор / является интуитивно понятным сокращением для метода joinpath().
- Откройте файл
main.pyв проводнике файлов слева. - Замените содержимое
main.pyследующим кодом для объединения компонентов пути и вывода результата.
from pathlib import Path
## Объединение компонентов пути с использованием оператора /
my_path = Path('usr') / 'bin' / 'spam'
print(my_path)
- Запустите скрипт из терминала:
python3 main.py
Вы увидите следующий вывод, который демонстрирует, как pathlib корректно формирует путь в стиле Unix:
usr/bin/spam
Далее, давайте получим текущий рабочий каталог с помощью Path.cwd().
- Замените содержимое
main.pyследующим кодом (это заменит предыдущий код):
from pathlib import Path
## Получение текущего рабочего каталога
cwd = Path.cwd()
print(cwd)
- Снова запустите скрипт:
python3 main.py
Вывод покажет текущий рабочий каталог, который в данной среде равен /home/labex/project:
/home/labex/project
Наконец, давайте создадим новый каталог. Возникнет FileNotFoundError, если вы попытаетесь создать каталог, родительский каталог которого не существует. Чтобы избежать этого, используйте mkdir(parents=True), который создает все необходимые родительские каталоги.
- Замените содержимое
main.pyследующим кодом для создания вложенной структуры каталогов:
from pathlib import Path
## Получение текущего рабочего каталога
cwd = Path.cwd()
## Определение пути для новых вложенных каталогов
new_dirs = cwd / 'delicious' / 'walnut' / 'waffles'
## Создание каталогов, включая любые отсутствующие родительские
new_dirs.mkdir(parents=True, exist_ok=True)
print(f"Successfully created: {new_dirs}")
Аргумент
exist_ok=Trueпредотвращает ошибку, если каталог уже существует. Это означает, что вы можете запускать скрипт несколько раз без ошибок.
- Запустите скрипт:
python3 main.py
Скрипт создаст вложенные каталоги и выведет подтверждающее сообщение:
Successfully created: /home/labex/project/delicious/walnut/waffles
Вы можете проверить создание каталога, выполнив в терминале команду ls -R delicious.
Абсолютные и относительные пути
На этом этапе вы научитесь различать абсолютные и относительные пути и работать с ними.
- Абсолютный путь (absolute path) начинается от корня файловой системы (например,
/в Linux илиC:\в Windows). - Относительный путь (relative path) определяется относительно текущего рабочего каталога.
pathlib предоставляет методы для проверки того, является ли путь абсолютным, для приведения пути к его абсолютному виду и для нахождения относительного пути от отправной точки.
Сначала давайте проверим, является ли путь абсолютным, используя is_absolute().
- Замените содержимое
main.pyследующим кодом:
from pathlib import Path
## Проверка, являются ли пути абсолютными
print(f"Path('/') is absolute: {Path('/').is_absolute()}")
print(f"Path('..') is absolute: {Path('..').is_absolute()}")
- Запустите скрипт:
python3 main.py
Вывод подтверждает, что / является абсолютным путем, в то время как .. (родительский каталог) — относительным.
Path('/') is absolute: True
Path('..') is absolute: False
Далее вы можете получить полный абсолютный путь относительного пути, используя resolve(). Это полезно для преобразования относительных путей, таких как . (текущий каталог) или .. (родительский каталог), в их полную, однозначную форму.
- Замените содержимое
main.pyследующим кодом:
from pathlib import Path
## Приведение относительного пути к его абсолютному виду
## '..' ссылается на родительский каталог текущего рабочего каталога
resolved_path = Path('..').resolve()
print(f"Current working directory: {Path.cwd()}")
print(f"Resolved path of '..': {resolved_path}")
- Выполните скрипт:
python3 main.py
Вывод покажет текущий каталог и приведенный к абсолютному виду путь к его родителю.
Current working directory: /home/labex/project
Resolved path of '..': /home/labex
Наконец, вы можете вычислить относительный путь от одного пути к другому, используя relative_to().
- Замените содержимое
main.pyследующим кодом для вычисления относительного пути:
from pathlib import Path
## Получение относительного пути от отправной точки
## Это показывает путь к '/etc/passwd' относительно корневого каталога '/'
relative_path = Path('/etc/passwd').relative_to('/')
print(f"Path to '/etc/passwd' relative to '/': {relative_path}")
- Запустите скрипт:
python3 main.py
Вывод показывает путь относительно корневого каталога:
Path to '/etc/passwd' relative to '/': etc/passwd
Проверка и инспекция путей
На этом этапе вы узнаете, как проверять свойства пути, например, существует ли он и является ли он файлом или каталогом. Вы также узнаете, как извлекать метаданные файла, такие как его размер.
Распространенная задача — убедиться, что файл или каталог существует, прежде чем пытаться получить к нему доступ. Метод exists() идеально подходит для этого.
- Откройте
main.pyи замените его содержимое следующим кодом. Он проверит существование текущего каталога (.), файла (setup.py) и несуществующего файла.
from pathlib import Path
## Проверка существования путей
print(f"Path('.') exists: {Path('.').exists()}")
print(f"Path('setup.py') exists: {Path('setup.py').exists()}")
print(f"Path('nonexistentfile') exists: {Path('nonexistentfile').exists()}")
- Запустите скрипт:
python3 main.py
Вывод подтвердит, какие пути существуют:
Path('.') exists: True
Path('setup.py') exists: True
Path('nonexistentfile') exists: False
Вы также можете быть более конкретными и проверить, указывает ли путь на файл с помощью is_file(), или на каталог с помощью is_dir().
- Замените содержимое
main.pyследующим кодом для выполнения этих проверок:
from pathlib import Path
## Проверка, является ли путь файлом
print(f"Path('setup.py').is_file(): {Path('setup.py').is_file()}")
print(f"Path('/home').is_file(): {Path('/home').is_file()}")
## Проверка, является ли путь каталогом
print(f"Path('/').is_dir(): {Path('/').is_dir()}")
print(f"Path('setup.py').is_dir(): {Path('setup.py').is_dir()}")
- Выполните скрипт:
python3 main.py
Вывод корректно определит тип каждого пути:
Path('setup.py').is_file(): True
Path('/home').is_file(): False
Path('/').is_dir(): True
Path('setup.py').is_dir(): False
Наконец, чтобы получить подробную информацию о файле, например, его размер, вы можете использовать метод stat(). Он возвращает объект с различными атрибутами, включая st_size для размера файла в байтах.
- Замените содержимое
main.pyследующим кодом, чтобы получить размер системного файла:
from pathlib import Path
import os
## Определение пути к файлу
file_path = Path('/bin/python3')
## Получение статистики файла
stat_info = file_path.stat()
## Вывод размера в байтах
print(f"File: {file_path}")
print(f"Size: {stat_info.st_size} bytes")
- Запустите скрипт:
python3 main.py
Вывод отобразит размер исполняемого файла Python в байтах. Точный размер может варьироваться.
File: /bin/python3
Size: 10024 bytes
Управление файлами и папками
На этом шаге вы научитесь выполнять общие операции с файловой системой, такие как копирование, перемещение, переименование и удаление файлов и папок, используя модуль shutil в сочетании с pathlib.
Модуль shutil предоставляет функции высокого уровня для файловых операций. Начнем с копирования файлов. shutil.copy() копирует один файл в новое местоположение.
- Откройте файл
manage_files.pyв проводнике файлов слева. - Замените содержимое
manage_files.pyследующим кодом. Это скопируетspam.txtв каталог/tmp/delicious.
import shutil
from pathlib import Path
## Define source and destination
source_file = Path('/tmp/spam.txt')
destination_dir = Path('/tmp/delicious')
## Copy the file
shutil.copy(source_file, destination_dir)
print(f"Copied '{source_file}' to '{destination_dir}'")
## You can also rename the file while copying
shutil.copy('/tmp/eggs.txt', '/tmp/delicious/eggs2.txt')
print("Copied and renamed 'eggs.txt' to 'eggs2.txt'")
- Запустите скрипт:
python3 manage_files.py
Чтобы скопировать все дерево каталогов, используйте shutil.copytree().
- Добавьте следующие строки в конец
manage_files.py(добавьте их после существующего кода):
## Copy an entire directory tree
shutil.copytree('/tmp/bacon', '/tmp/bacon_backup')
print("Copied directory '/tmp/bacon' to '/tmp/bacon_backup'")
Примечание: Если вы запустите этот скрипт несколько раз,
shutil.copytree()завершится ошибкой, поскольку целевой каталог уже существует. Это ожидаемое поведение. Пока просто запустите его один раз.
- Запустите скрипт еще раз, чтобы увидеть результат.
Далее рассмотрим перемещение и переименование файлов с помощью shutil.move(). Эта функция может либо переместить файл в другой каталог, либо переименовать его.
- Замените все содержимое
manage_files.pyследующим кодом, чтобы переместитьbacon.txt:
import shutil
from pathlib import Path
## Create the destination directory if it doesn't exist
Path('/tmp/eggs').mkdir(parents=True, exist_ok=True)
## Move a file to a directory (only if source exists)
bacon_source = Path('/tmp/bacon.txt')
if bacon_source.exists():
shutil.move('/tmp/bacon.txt', '/tmp/eggs')
print("Moved '/tmp/bacon.txt' to '/tmp/eggs'")
else:
print("'/tmp/bacon.txt' not found (may have been moved already)")
## Move and rename a file
## First, let's recreate the file to move
Path('/tmp/bacon.txt').touch()
shutil.move('/tmp/bacon.txt', '/tmp/eggs/new_bacon.txt')
print("Moved and renamed '/tmp/bacon.txt' to '/tmp/eggs/new_bacon.txt'")
Примечание: Код проверяет существование исходного файла перед его перемещением, поэтому вы можете безопасно запускать этот скрипт несколько раз. Вторая часть всегда будет создавать новый файл и перемещать его.
- Запустите скрипт.
Наконец, рассмотрим удаление файлов и папок.
Path.unlink()удаляет один файл.Path.rmdir()удаляет пустой каталог.shutil.rmtree()удаляет каталог и все его содержимое.
- Замените все содержимое
manage_files.pyследующим:
from pathlib import Path
import shutil
## Create a file to delete
file_to_delete = Path('/tmp/deleteme.txt')
file_to_delete.touch()
print(f"Created '{file_to_delete}'")
## Delete the file
file_to_delete.unlink()
print(f"Deleted '{file_to_delete}'")
## Create a directory to delete
dir_to_delete = Path('/tmp/temp_dir')
dir_to_delete.mkdir(exist_ok=True)
## rmdir() only works on empty directories
dir_to_delete.rmdir()
print(f"Deleted empty directory '{dir_to_delete}'")
## Use shutil.rmtree() to delete a directory and its contents
## Note: This will fail if the directory doesn't exist, which is fine for this example
if Path('/tmp/bacon_backup').exists():
shutil.rmtree('/tmp/bacon_backup')
print("Deleted directory '/tmp/bacon_backup' and all its contents")
else:
print("Directory '/tmp/bacon_backup' does not exist (may have been deleted already)")
Примечание: Если вы запустите этот скрипт несколько раз, некоторые операции могут завершиться ошибкой, поскольку файлы или каталоги уже были удалены. Это нормально и демонстрирует, что эти операции не являются идемпотентными (их нельзя безопасно повторять).
- Запустите скрипт для выполнения операций удаления.
Обход каталогов
На этом шаге вы узнаете, как перечислять содержимое каталога и рекурсивно обходить дерево каталогов.
Для перечисления содержимого каталога можно использовать метод iterdir(), который возвращает итератор всех элементов (файлов и каталогов) по указанному пути.
- Сначала настроим пример структуры каталогов. Выполните эту команду в терминале:
mkdir -p ~/project/data/level1 && touch ~/project/data/file0.txt ~/project/data/level1/file1.txt
- Теперь откройте
main.pyи замените его содержимое следующим кодом для вывода содержимого каталога/usr/bin.
from pathlib import Path
## Вывод содержимого каталога
print("Contents of '/usr/bin':")
count = 0
for f in Path('/usr/bin').iterdir():
if count < 5: ## Выводим первые 5 элементов, чтобы вывод был коротким
print(f)
count += 1
else:
break
- Запустите скрипт:
python3 main.py
Вывод покажет первые несколько файлов и каталогов внутри /usr/bin. Точный список может отличаться.
Contents of '/usr/bin':
/usr/bin/[
/usr/bin/addpart
/usr/bin/apt
/usr/bin/apt-cache
/usr/bin/apt-cdrom
Вы можете скомбинировать iterdir() с stat(), чтобы вычислить общий размер всех файлов в каталоге.
- Замените содержимое
main.pyследующим кодом. Обратите внимание, что этот код суммирует размеры только прямых потомков и не рекурсивно спускается в подкаталоги.
from pathlib import Path
total_size = 0
directory = Path('/usr/bin')
for sub_path in directory.iterdir():
if sub_path.is_file(): ## Убеждаемся, что добавляем только размер файлов
total_size += sub_path.stat().st_size
print(f"Total size of files in '{directory}': {total_size} bytes")
- Выполните скрипт. Он выведет общий размер всех файлов, находящихся непосредственно в
/usr/bin.
Для более сложных задач вам может потребоваться "пройти" (walk) по всему дереву каталогов, посещая все подкаталоги и файлы. Метод rglob() идеально подходит для этого. Он рекурсивно находит все пути, соответствующие заданному шаблону. Использование * в качестве шаблона соответствует всему.
- Сначала создайте более сложную структуру каталогов для этого примера. Выполните это в терминале:
mkdir -p /tmp/delicious/cats /tmp/delicious/walnut/waffles
touch /tmp/delicious/spam.txt /tmp/delicious/cats/catnames.txt /tmp/delicious/walnut/waffles/butter.txt
Примечание: Вы можете безопасно запускать эти команды несколько раз. Команда
mkdir -pне вызовет ошибку, если каталоги уже существуют, аtouchобновит временные метки файлов, если файлы уже существуют.
- Замените содержимое
main.pyследующим кодом для обхода каталога/tmp/delicious:
from pathlib import Path
p = Path('/tmp/delicious')
print(f"Walking directory tree for: {p}")
for i in p.rglob('*'):
print(i)
- Запустите скрипт:
python3 main.py
Вывод будет содержать список каждого файла и папки внутри /tmp/delicious и его подкаталогов, демонстрируя, как rglob() обходит все дерево.
Walking directory tree for: /tmp/delicious
/tmp/delicious/spam.txt
/tmp/delicious/eggs2.txt
/tmp/delicious/cats
/tmp/delicious/walnut
/tmp/delicious/cats/catnames.txt
/tmp/delicious/walnut/waffles
/tmp/delicious/walnut/waffles/butter.txt
Резюме
Поздравляем с завершением лабораторной работы! Вы научились эффективно управлять путями к файлам и каталогам в Python, используя современный модуль pathlib.
В ходе этой лабораторной работы вы практиковались в следующем:
- Создание и объединение путей кроссплатформенным способом с использованием объектов
Pathи оператора/. - Получение текущего рабочего каталога и создание вложенных каталогов с помощью
mkdir(parents=True). - Различие между абсолютными и относительными путями и преобразование между ними с использованием
is_absolute(),resolve()иrelative_to(). - Проверка путей с помощью
exists(),is_file()иis_dir(), а также инспекция метаданных файлов с помощьюstat(). - Выполнение основных задач по управлению файлами, таких как копирование, перемещение и удаление, с помощью модуля
shutilи методовpathlib. - Обход структур каталогов для вывода содержимого с помощью
iterdir()и рекурсивного поиска элементов с помощьюrglob().
Эти навыки являются основополагающими для написания надежных и поддерживаемых Python-приложений, которые взаимодействуют с файловой системой.



