Python · Синтаксис · Проміжний

pathlib

10 завдань

Навчіться працювати з файловими шляхами за допомогою сучасного модуля pathlib у Python — чистіший і потужніший за os.path.

Path-об'єкти та навігація

#
`pathlib` надає клас `Path` — об'єктно-орієнтований спосіб роботи з файловими шляхами. Він замінює рядкові маніпуляції `os.path` чистим, читабельним доступом до атрибутів. ```python from pathlib import Path ``` ## Створення Path-об'єктів ```python p = Path("/home/user/documents/report.txt") p = Path(".") # поточна директорія p = Path.home() # домашня директорія: /home/user p = Path.cwd() # поточна робоча директорія # Шляхи Windows теж працюють: p = Path("C:/Users/Alice/Desktop/file.txt") ``` ## Атрибути Path ```python p = Path("/home/user/projects/script.py") print(p.name) # "script.py" — ім'я файлу з розширенням print(p.stem) # "script" — ім'я файлу без розширення print(p.suffix) # ".py" — розширення з крапкою print(p.suffixes) # [".py"] — всі розширення (для ".tar.gz" → [".tar", ".gz"]) print(p.parent) # /home/user/projects — батьківська директорія print(p.parts) # ("/", "home", "user", "projects", "script.py") print(p.root) # "/" ``` ## Об'єднання шляхів Використовуйте оператор `/` — набагато чистіший за `os.path.join`: ```python base = Path("/home/user") config = base / "config" / "settings.json" print(config) # /home/user/config/settings.json # Праворуч можна передавати рядки reports = base / "reports" report_file = reports / "2024" / "q1.csv" ``` ## Перевірка існування ```python p = Path("/home/user/data.txt") print(p.exists()) # True/False — файл або директорія існує print(p.is_file()) # True якщо існує і є звичайним файлом print(p.is_dir()) # True якщо існує і є директорією print(p.is_symlink()) # True якщо є символічним посиланням ``` ## Конвертація та порівняння ```python p = Path("/home/user/file.txt") # Перетворення у рядок str(p) # "/home/user/file.txt" p.as_posix() # "/home/user/file.txt" — завжди косі риски вперед # Абсолютний шлях p.resolve() # розв'язує символічні посилання, повертає абсолютний Path # Відносний шлях p.relative_to("/home/user") # Path("file.txt") ```

Читання, запис та пошук файлів

#
## Читання та запис файлів Об'єкти `Path` можуть читати і записувати текст та байти безпосередньо — для простих випадків `open()` не потрібен: ```python from pathlib import Path p = Path("notes.txt") # Запис тексту p.write_text("Hello, pathlib!\n", encoding="utf-8") # Читання тексту content = p.read_text(encoding="utf-8") print(content) # Hello, pathlib! # Читання байтів data = p.read_bytes() # Запис байтів p.write_bytes(b"\x00\x01\x02") ``` Для читання рядок за рядком використовуйте `open()` з Path: ```python with p.open("r", encoding="utf-8") as f: for line in f: print(line.strip()) ``` ## Перегляд вмісту директорії ```python d = Path("/home/user/projects") # Всі записи (файли і піддиректорії) for item in d.iterdir(): print(item.name, "dir" if item.is_dir() else "file") # Тільки файли files = [f for f in d.iterdir() if f.is_file()] # Тільки директорії subdirs = [f for f in d.iterdir() if f.is_dir()] ``` ## glob: пошук за шаблоном ```python src = Path("/home/user/projects") # Всі Python-файли у директорії (без рекурсії) py_files = list(src.glob("*.py")) # Всі Python-файли рекурсивно (** = будь-яка кількість директорій) all_py = list(src.rglob("*.py")) # Всі лог-файли у будь-якій піддиректорії "logs" logs = list(src.glob("**/logs/*.log")) ``` ## Створення директорій ```python new_dir = Path("/home/user/projects/new_feature") # Створити одну директорію new_dir.mkdir() # Створити батьківські директорії якщо не існують, без помилки якщо вже є new_dir.mkdir(parents=True, exist_ok=True) ``` ## Перейменування та видалення ```python p = Path("old_name.txt") # Перейменувати (повертає новий Path) new_p = p.rename("new_name.txt") print(new_p) # new_name.txt # Видалити файл p.unlink() # Видалити порожню директорію d = Path("empty_dir") d.rmdir() # Python 3.8+: missing_ok p.unlink(missing_ok=True) # без помилки якщо не знайдено ``` ## Практичний приклад: обробити всі CSV-файли ```python data_dir = Path("data") output_dir = Path("output") output_dir.mkdir(exist_ok=True) for csv_file in data_dir.glob("*.csv"): content = csv_file.read_text() processed = content.upper() # умовна трансформація out_file = output_dir / csv_file.name out_file.write_text(processed) print(f"Оброблено: {csv_file.name}") ```

Довідник: pathlib

#
## Атрибути Path (тільки для читання) | Атрибут | Приклад значення | Що повертає | |---|---|---| | `p.name` | `"script.py"` | Ім'я файлу з розширенням | | `p.stem` | `"script"` | Ім'я файлу без розширення | | `p.suffix` | `".py"` | Останнє розширення (з крапкою) | | `p.suffixes` | `[".tar", ".gz"]` | Всі розширення | | `p.parent` | `Path("/home/user")` | Path батьківської директорії | | `p.parents` | (ітерабельний) | Всі батьківські директорії | | `p.parts` | `("/", "home", "user", "f.py")` | Кортеж компонентів шляху | | `p.root` | `"/"` | Кореневий компонент | ## Методи Path — запити | Метод | Що робить | |---|---| | `p.exists()` | True якщо шлях існує (файл або директорія) | | `p.is_file()` | True якщо існує і є звичайним файлом | | `p.is_dir()` | True якщо існує і є директорією | | `p.is_symlink()` | True якщо символічне посилання | | `p.stat()` | os.stat_result (розмір, mtime тощо) | | `p.resolve()` | Абсолютний шлях з розв'язаними посиланнями | | `p.relative_to(other)` | Відносний шлях від `other` | | `p.with_name(name)` | Новий шлях з іншим ім'ям | | `p.with_stem(stem)` | Новий шлях з іншим stem | | `p.with_suffix(suf)` | Новий шлях з іншим розширенням | ## Методи Path — файловий ввід/вивід | Метод | Що робить | |---|---| | `p.read_text(encoding=...)` | Прочитати вміст як str | | `p.write_text(s, encoding=...)` | Записати str | | `p.read_bytes()` | Прочитати вміст як bytes | | `p.write_bytes(b)` | Записати bytes | | `p.open(mode, encoding=...)` | Відкрити файловий об'єкт | ## Методи Path — операції з ФС | Метод | Що робить | |---|---| | `p.mkdir(parents=False, exist_ok=False)` | Створити директорію | | `p.rename(target)` | Перейменувати, повертає новий Path | | `p.replace(target)` | Перейменувати, перезаписати якщо є | | `p.unlink(missing_ok=False)` | Видалити файл або посилання | | `p.rmdir()` | Видалити порожню директорію | | `p.touch()` | Створити порожній файл або оновити mtime | | `p.iterdir()` | Ітерувати по вмісту директорії | | `p.glob(pattern)` | Знайти файли за шаблоном | | `p.rglob(pattern)` | Рекурсивний glob | ## Шаблони glob | Шаблон | Що відповідає | |---|---| | `*` | Будь-яке ім'я у поточній директорії | | `*.py` | Python-файли у поточній директорії | | `**/*.py` | Python-файли рекурсивно | | `??.txt` | Двосимвольне ім'я + .txt |
01

Атрибути 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")
02

Побудова шляху через /

#

Напишіть функцію `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
03

Змінити розширення

#

Напишіть функцію `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
04

Безпечне читання файлу

#

Напишіть функцію `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
05

Записати і перевірити

#

Напишіть функцію `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
06

Підрахунок файлів за розширенням

#

Напишіть функцію `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}
07

Знайти великі файли

#

Напишіть функцію `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)
08

Гарантувати директорію

#

Напишіть функцію `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
09

Резервна копія файлу

#

Напишіть функцію `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
10

Знайти 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)