Python · Синтаксис · Промежуточный
pathlib
Научитесь работать с путями файловой системы с помощью современного модуля pathlib в Python — чище и мощнее, чем os.path.
Краткое введение в тему и пояснения перед упражнениями (упражнения ниже):
Чтение, запись и поиск файлов
#Справочник: pathlib
#Упражнения:
Атрибуты Path
#Имея строку пути `"/home/user/projects/data/report_2024.csv"`, создайте объект `Path` из неё и выведите: - Полное имя файла (`name`) - Имя без расширения (`stem`) - Расширение (`suffix`) - Родительскую директорию (`parent`) - Все компоненты пути (`parts`)
from pathlib import Path
p = Path("/home/user/projects/data/report_2024.csv")
# Выведите: name, stem, suffix, parent, parts
print(p.name) # ???
print(p.stem) # ???
print(p.suffix) # ???
print(p.parent) # ???
print(p.parts) # ???
Решение
from pathlib import Path
p = Path("/home/user/projects/data/report_2024.csv")
print(p.name) # report_2024.csv
print(p.stem) # report_2024
print(p.suffix) # .csv
print(p.parent) # /home/user/projects/data
print(p.parts) # ("/", "home", "user", "projects", "data", "report_2024.csv")
Построение пути через /
#Напишите функцию `build_path(base, *parts)`, которая принимает базовую директорию как строку и любое количество частей пути, соединяет их через оператор `/` и возвращает итоговый объект `Path`. Пример: `build_path("/home/user", "projects", "myapp", "config.json")` → `Path("/home/user/projects/myapp/config.json")`.
from pathlib import Path
def build_path(base, *parts):
pass
result = build_path("/home/user", "projects", "myapp", "config.json")
print(result) # /home/user/projects/myapp/config.json
Решение
from pathlib import Path
def build_path(base, *parts):
p = Path(base)
for part in parts:
p = p / part
return p
# Короче: Path(base).joinpath(*parts)
result = build_path("/home/user", "projects", "myapp", "config.json")
print(result) # /home/user/projects/myapp/config.json
Изменить расширение
#Напишите функцию `change_extension(path_str, new_ext)`, которая принимает строку пути и новое расширение (с точкой, например `".txt"`), и возвращает новый `Path` с изменённым расширением. Пример: `change_extension("/home/user/data.csv", ".json")` → `Path("/home/user/data.json")`. Используйте `p.with_suffix()`.
from pathlib import Path
def change_extension(path_str, new_ext):
pass
print(change_extension("/home/user/data.csv", ".json"))
# /home/user/data.json
print(change_extension("/docs/report.txt", ".md"))
# /docs/report.md
Решение
from pathlib import Path
def change_extension(path_str, new_ext):
return Path(path_str).with_suffix(new_ext)
print(change_extension("/home/user/data.csv", ".json"))
# /home/user/data.json
print(change_extension("/docs/report.txt", ".md"))
# /docs/report.md
Безопасное чтение файла
#Напишите функцию `safe_read(path_str, encoding="utf-8")`, которая читает содержимое файла по `path_str` и возвращает его как строку. Если файл не существует — возвращает `None`. При любой другой ошибке — тоже `None`. Используйте `Path.read_text()`.
from pathlib import Path
def safe_read(path_str, encoding="utf-8"):
pass
Path("/tmp/test_pathlib.txt").write_text("hello", encoding="utf-8")
print(safe_read("/tmp/test_pathlib.txt")) # hello
print(safe_read("/tmp/missing.txt")) # None
Решение
from pathlib import Path
def safe_read(path_str, encoding="utf-8"):
try:
return Path(path_str).read_text(encoding=encoding)
except Exception:
return None
Path("/tmp/test_pathlib.txt").write_text("hello", encoding="utf-8")
print(safe_read("/tmp/test_pathlib.txt")) # hello
print(safe_read("/tmp/missing.txt")) # None
Записать и проверить
#Напишите функцию `write_and_verify(path_str, content)`, которая: 1. Записывает `content` в файл по `path_str` через `Path.write_text()` 2. Читает обратно через `Path.read_text()` 3. Возвращает `True` если содержимое совпадает, `False` иначе Используйте `encoding="utf-8"` для обеих операций.
from pathlib import Path
def write_and_verify(path_str, content):
pass
print(write_and_verify("/tmp/verify_test.txt", "Hello pathlib!"))
# True
Решение
from pathlib import Path
def write_and_verify(path_str, content):
p = Path(path_str)
p.write_text(content, encoding="utf-8")
return p.read_text(encoding="utf-8") == content
print(write_and_verify("/tmp/verify_test.txt", "Hello pathlib!"))
# True
Подсчёт файлов по расширению
#Напишите функцию `count_by_extension(dir_path)`, которая принимает строку пути к директории и возвращает словарь, где каждое расширение файла соответствует количеству файлов с этим расширением в директории (без рекурсии). Пример: если директория содержит `a.py`, `b.py`, `c.txt`, `d.txt`, `e.md`, результат: `{".py": 2, ".txt": 2, ".md": 1}`.
from pathlib import Path
def count_by_extension(dir_path):
pass
# Пример: если в директории a.py, b.py, c.txt, d.txt, e.md
# результат: {".py": 2, ".txt": 2, ".md": 1}
Решение
from pathlib import Path
def count_by_extension(dir_path):
counts = {}
for p in Path(dir_path).iterdir():
if p.is_file():
ext = p.suffix
counts[ext] = counts.get(ext, 0) + 1
return counts
import tempfile
with tempfile.TemporaryDirectory() as d:
for name in ["a.py", "b.py", "c.txt", "d.txt", "e.md"]:
Path(d, name).write_text("", encoding="utf-8")
print(count_by_extension(d))
# {".py": 2, ".txt": 2, ".md": 1}
Найти большие файлы
#Напишите функцию `find_large_files(dir_path, min_size_bytes)`, которая рекурсивно ищет в директории все файлы больше `min_size_bytes` и возвращает их пути как отсортированный список объектов `Path`. Используйте `p.rglob("*")` и `p.stat().st_size`.
from pathlib import Path
def find_large_files(dir_path, min_size_bytes):
pass
# Возвращает отсортированный список Path-объектов для файлов > min_size_bytes
Решение
from pathlib import Path
def find_large_files(dir_path, min_size_bytes):
return sorted(
p for p in Path(dir_path).rglob("*")
if p.is_file() and p.stat().st_size > min_size_bytes
)
for f in find_large_files(".", 1000):
print(f)
Гарантировать директорию
#Напишите функцию `ensure_dir(path_str)`, которая создаёт директорию по `path_str` если она не существует. Она должна: - Создавать все промежуточные родительские директории по необходимости - Не бросать ошибку если директория уже существует - Возвращать объект `Path` директории Используйте `Path.mkdir(parents=True, exist_ok=True)`.
from pathlib import Path
def ensure_dir(path_str):
pass
p = ensure_dir("/tmp/myapp/logs/2024")
print(p.is_dir()) # True
print(p) # /tmp/myapp/logs/2024
Решение
from pathlib import Path
def ensure_dir(path_str):
p = Path(path_str)
p.mkdir(parents=True, exist_ok=True)
return p
p = ensure_dir("/tmp/myapp/logs/2024")
print(p.is_dir()) # True
print(p) # /tmp/myapp/logs/2024
Резервная копия файла
#Напишите функцию `backup(path_str)`, которая создаёт резервную копию файла добавляя `.bak` к его имени. Если файл не существует — бросает `FileNotFoundError`. Возвращает `Path` резервного файла. Пример: `backup("/home/user/data.txt")` создаёт `/home/user/data.txt.bak` и возвращает этот путь.
from pathlib import Path
import shutil
def backup(path_str):
pass
Path("/tmp/data_original.txt").write_text("important", encoding="utf-8")
bak = backup("/tmp/data_original.txt")
print(bak) # /tmp/data_original.txt.bak
Решение
from pathlib import Path
import shutil
def backup(path_str):
p = Path(path_str)
if not p.exists():
raise FileNotFoundError(f"File not found: {p}")
bak = p.with_suffix(p.suffix + ".bak")
shutil.copy2(str(p), str(bak))
return bak
Path("/tmp/data_original.txt").write_text("important", encoding="utf-8")
bak = backup("/tmp/data_original.txt")
print(bak) # /tmp/data_original.txt.bak
print(bak.read_text(encoding="utf-8")) # important
Найти Python-файлы
#Напишите функцию `find_python_files(start_dir)`, которая рекурсивно находит все файлы `.py` в `start_dir`, сортирует их по имени и возвращает список их относительных путей (относительно `start_dir`) как строки. Используйте `Path.rglob("*.py")` и `p.relative_to(start_dir)`.
from pathlib import Path
def find_python_files(start_dir):
pass
# Возвращает список относительных строк пути отсортированных по имени
for f in find_python_files("."):
print(f)
Решение
from pathlib import Path
def find_python_files(start_dir):
base = Path(start_dir)
return sorted(
str(p.relative_to(base))
for p in base.rglob("*.py")
)
for f in find_python_files("."):
print(f)