ru
Feedback
Python для начинающих

Python для начинающих

Открыть в Telegram

Python для начинающих

Больше
1 239
Подписчики
Нет данных24 часа
+17 дней
-130 день
Архив постов
Используем shutil: копируем и удаляем файлы без боли Работа с файлами в Python часто начинается с модуля os, но как только дело доходит до копирования и целых папок — на сцену выходит shutil. Это такой «швейцарский нож» для файловой системы. Подключается просто:
import shutil
from pathlib import Path
## Копирование файлов Базовый вариант — shutil.copy():
src = Path("data/source.txt")
dst = Path("backup/source_copy.txt")

shutil.copy(src, dst)
copy переносит содержимое и права доступа, но не метаданные (например, время изменения). Если нужны максимально точные «клоны» файла, есть copy2:
shutil.copy2(src, dst)
Разница — в сохранении метаданных (atime, mtime и т.п.). Для бэкапов это часто критично. ## Копирование папок Для директорий используется copytree:
src_dir = Path("data")
dst_dir = Path("data_backup")

shutil.copytree(src_dir, dst_dir)
Важно: если data_backup уже существует, будет ошибка. В Python 3.8+ можно указать dirs_exist_ok=True:
shutil.copytree(src_dir, dst_dir, dirs_exist_ok=True)
Так можно «обновлять» уже существующую папку бэкапа. ## Удаление файлов и папок Одиночные файлы удобнее удалять через Path.unlink(), а вот для целых деревьев директорий есть rmtree:
target_dir = Path("old_logs")

shutil.rmtree(target_dir)
Команда безвозвратная: корзины не будет, поэтому перед вызовом стоит дважды проверить путь или добавить «защиту от дурака»:
if "backup" in str(target_dir):
    shutil.rmtree(target_dir)
## Мини-утилита «скопировать и почистить» Соберём всё вместе: скопируем папку и удалим старую:
def move_folder(src: Path, dst: Path):
    shutil.copytree(src, dst, dirs_exist_ok=True)
    shutil.rmtree(src)

move_folder(Path("tmp_uploads"), Path("storage/uploads"))
Фактически мы реализовали аналог «перемещения» папки силами shutil. --- shutil хорош тем, что закрывает 80% задач по управлению файлами: копирование, рекурсивные операции, бэкапы, чистка временных директорий. А главное — код остаётся коротким и читаемым, без ручного обхода каталогов и велосипедов на os.listdir().

Использование shutil для копирования и удаления файлов и папок
Использование shutil для копирования и удаления файлов и папок

Генерация диаграмм с pygraphviz: основы визуализации графов Иногда проще один раз увидеть, чем сто раз распечатать print(). Особенно когда дело касается связей: кто с кем соединен, в каком направлении идет поток данных, как устроена архитектура проекта. Для этого отлично подходит pygraphviz — оболочка над знаменитым Graphviz, позволяющая генерировать диаграммы прямо из Python. --- ### Установка
pip install pygraphviz
Важно: на некоторых системах сначала нужно установить сам Graphviz (через пакетный менеджер ОС). --- ### Простейший граф Создадим ориентированный граф и сохраним его как PNG:
from pygraphviz import AGraph

g = AGraph(directed=True)

g.add_node("User")
g.add_node("API")
g.add_node("DB")

g.add_edge("User", "API")
g.add_edge("API", "DB")

g.layout(prog="dot")      # алгоритм раскладки
g.draw("simple_graph.png")
prog="dot" — классический и самый читаемый для иерархических структур (запросы сверху, база снизу). --- ### Стайлинг: делаем диаграмму понятной Граф без стиля — это просто клубок линий. Добавим цвета и формы, чтобы по диаграмме можно было ориентироваться за секунды:
from pygraphviz import AGraph

g = AGraph(directed=True, strict=True, rankdir="LR")  # слева направо

g.add_node("Client", shape="box", style="filled", fillcolor="#AED6F1")
g.add_node("Service", shape="ellipse", style="filled", fillcolor="#A9DFBF")
g.add_node("Cache", shape="diamond", style="filled", fillcolor="#F9E79F")

g.add_edge("Client", "Service", label="HTTP")
g.add_edge("Service", "Cache", label="GET")
g.add_edge("Cache", "Service", label="HIT", color="green")
g.add_edge("Service", "Client", label="Response", color="blue")

g.graph_attr.update(label="Request Flow", fontsize="20")
g.layout(prog="dot")
g.draw("styled_graph.png")
Ключевые идеи: - rankdir="LR" — направление слева направо (удобно для потоков). - shape, fillcolor, style — визуальное кодирование типов узлов. - label и color у ребер помогают понимать протоколы, типы взаимодействия и т.п. --- ### Быстрая визуализация структур из кода pygraphviz удобно использовать для генерации диаграмм по данным из программы: граф зависимостей модулей, цепочка этапов обработки данных, схема микросервисов. Достаточно обхода вашей структуры (словаря, списка связей, дерева) и вызова add_node / add_edge в цикле. --- pygraphviz хорош тем, что избавляет от ручного рисования схем в редакторах: диаграмма становится частью кода, обновляется автоматически и всегда соответствует реальности. Для начинающего питониста это отличный инструмент, чтобы увидеть свои структуры данных и архитектуру, а не только представлять их в голове.

Генерация диаграмм с pygraphviz: основы визуализации графов
Генерация диаграмм с pygraphviz: основы визуализации графов

### Работа с буфером ввода-вывода с помощью io.StringIO Иногда хочется поработать с текстом «как с файлом», но без создания настоящего файла на диске. Для этого в Python есть удобный инструмент — io.StringIO. Это такой «файлик в памяти»: его можно читать, писать в него, перематывать курсор — почти как с обычным файлом. --- ### Зачем вообще нужен StringIO? Типичные сценарии: 1. Тестирование кода, который работает с файлами, без создания временных файлов. 2. Промежуточная обработка строк в виде потока: удобно, когда данные приходят частями. 3. Перенаправление вывода (например, print) в строку, чтобы потом её обработать. --- ### Базовый пример: пишем и читаем
from io import StringIO

buffer = StringIO()

buffer.write("Hello, world!\n")
buffer.write("This is in-memory file.\n")

# Перемещаем курсор в начало, как после открытия файла для чтения
buffer.seek(0)

content = buffer.read()
print(content)
Здесь buffer ведет себя почти как файл, но все хранится в оперативной памяти в виде строки. --- ### Построчное чтение
from io import StringIO

data = "line 1\nline 2\nline 3\n"
buffer = StringIO(data)

for line in buffer:
    print(line.strip())
Интерфейс знакомый: можно итерироваться по строкам, использовать readline(), readlines() и т.д. --- ### Перенаправление print в строку Иногда нужно собрать то, что обычно выводится в консоль:
from io import StringIO
import sys

buffer = StringIO()
stdout_backup = sys.stdout

try:
    sys.stdout = buffer
    print("First line")
    print("Second line")
finally:
    sys.stdout = stdout_backup

result = buffer.getvalue()
print("Captured:")
print(result)
buffer.getvalue() возвращает всю накопленную строку. Удобно для логирования и тестов. --- ### Важные нюансы - StringIO работает только с текстом (str). Для байтов используйте io.BytesIO. - Метод getvalue() можно вызывать сколько угодно раз — он не сдвигает курсор. - Не забывайте про seek(0), если хотите перечитать содержимое с начала. io.StringIO — отличный способ тренироваться с файловым API, не засоряя диск, и гибкий инструмент для чистого и удобного кода при работе с текстовыми потоками.

Работа с буфером ввода-вывода с помощью io.StringIO
Работа с буфером ввода-вывода с помощью io.StringIO

Создание локального веб-сервера с помощью встроенного http.server Когда слышишь «веб‑сервер», в голове всплывают сложные штуки вроде Nginx, Apache или хотя бы Flask. Но в Python уже есть крошечный сервер «из коробки» — модуль http.server. Он идеален для экспериментов, отладки и быстрых демо. --- ### Запускаем самый простой сервер Перейдите в папку с файлами, которые хотите раздавать, и запустите:
python -m http.server 8000
Теперь в браузере откройте http://localhost:8000 — вы уже подняли сервер, который раздает текущую директорию. Порт можно не указывать, тогда по умолчанию будет 8000. --- ### Минимальный сервер своим скриптом Можно сделать то же самое из Python‑файла:
from http.server import HTTPServer, SimpleHTTPRequestHandler

def run_server(port: int = 8000):
    server_address = ("", port)  # "" означает слушать на всех интерфейсах
    httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
    print(f"Serving on port {port}...")
    httpd.serve_forever()

if __name__ == "__main__":
    run_server()
SimpleHTTPRequestHandler умеет раздавать файлы из текущей папки и показывает простую HTML‑страницу со списком файлов. --- ### Свой обработчик запросов Хотите кастомный ответ вместо списка файлов? Наследуемся от BaseHTTPRequestHandler:
from http.server import HTTPServer, BaseHTTPRequestHandler

class HelloHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/":
            message = "Hello, Python beginner!"
        else:
            message = f"Unknown path: {self.path}"

        data = message.encode("utf-8")

        self.send_response(200)
        self.send_header("Content-Type", "text/plain; charset=utf-8")
        self.send_header("Content-Length", str(len(data)))
        self.end_headers()

        self.wfile.write(data)

def run_server(port: int = 8080):
    server_address = ("", port)
    httpd = HTTPServer(server_address, HelloHandler)
    print(f"Custom server on port {port}...")
    httpd.serve_forever()

if __name__ == "__main__":
    run_server()
Теперь при открытии http://localhost:8080/ вы получите текстовый ответ, а при переходе по другому пути — сообщение с этим путем. --- ### Когда это полезно - Быстро показать HTML/CSS/JS без настройки больших серверов. - Локально протестировать AJAX‑запросы. - Написать минимальный API‑мок для отладки клиента. http.server не для продакшена: нет защиты, логики масштабирования, нормального логирования. Но для обучения он идеально показывает, как HTTP‑запрос превращается в ответ, и помогает почувствовать «скелет» любого веб‑фреймворка.

Создание локального веб-сервера с помощью встроенного http.server
Создание локального веб-сервера с помощью встроенного http.server

Использование difflib: как Python видит отличия лучше нас Когда файл «чуть-чуть поменяли», глазами искать отличия — то еще удовольствие. Python-модуль difflib умеет делать это аккуратно, наглядно и… очень гибко. Разберёмся, как сравнивать строки и файлы, будто у нас встроен мини-Git. --- ### 1. Быстро сравнить две строки Начнем с простого: найти различия между двумя строками посимвольно.
import difflib

s1 = "Python is awesome!"
s2 = "Python is awesom?"

diff = difflib.ndiff(s1, s2)
print("\n".join(diff))
Результат покажет строки с префиксами: - - — удалено - + — добавлено - ? — подсказка, какие именно символы отличаются Это удобно, когда нужно понять, почему строки «почти одинаковы», но сравнение == возвращает False. --- ### 2. Похожесть строк в процентах Иногда важно не чем строки отличаются, а насколько они похожи. Для этого есть SequenceMatcher.
from difflib import SequenceMatcher

a = "color"
b = "colour"

ratio = SequenceMatcher(None, a, b).ratio()
print(ratio)  # например, 0.91...
Значение от 0 до 1 — коэффициент похожести. Можно использовать для: - поиска опечаток, - подсказки «Вы имели в виду…», - фильтрации «почти одинаковых» записей. --- ### 3. HTML-отчет о различиях Хотите красивый, наглядный diff в браузере? difflib.HtmlDiff как раз для этого:
import difflib

old_lines = [
    "def add(a, b):\n",
    "    return a + b\n",
]

new_lines = [
    "def add(a, b):\n",
    "    result = a + b\n",
    "    return result\n",
]

html = difflib.HtmlDiff().make_file(old_lines, new_lines,
                                    fromdesc="Old version",
                                    todesc="New version")

with open("diff.html", "w", encoding="utf-8") as f:
    f.write(html)
Откройте diff.html в браузере — увидите таблицу с подсветкой добавленных/удалённых строк. Мини-система контроля версий прямо в одном файле. --- ### 4. Сравнение файлов построчно Классический сценарий — сравнить два текстовых файла:
import difflib

with open("old.txt", encoding="utf-8") as f:
    old = f.readlines()
with open("new.txt", encoding="utf-8") as f:
    new = f.readlines()

for line in difflib.unified_diff(old, new,
                                 fromfile="old.txt",
                                 tofile="new.txt"):
    print(line.rstrip())
Формат unified diff знаком всем, кто работал с Git: строки с +, - и контекстом вокруг изменений. Удобно для логов, конфигов, автопроверок. --- difflib часто недооценивают, а зря. Это готовый швейцарский нож для работы с похожими текстами: от проверки орфографии до построения собственных дифф-отчетов. И всё это — в стандартной библиотеке, без единого pip install.

Использование difflib для поиска различий между строками и файлами
Использование difflib для поиска различий между строками и файлами

Модуль uuid: генерация уникальных идентификаторов без боли и коллизий Когда нужно что-то «уникальное» — многие программисты тянутся к времени, счётчикам или даже случайным числам. Проблема в том, что всё это легко пересекается, особенно в распределённых системах. Здесь в игру вступает модуль uuid — стандартный способ генерировать уникальные идентификаторы в Python. UUID (Universally Unique Identifier) — это 128-битное число, обычно записываемое как строка вроде: 550e8400-e29b-41d4-a716-446655440000 Вероятность коллизии настолько мала, что их смело используют в базах данных, API, токенах и временных файлах. --- ### Базовая генерация UUID
import uuid

user_id = uuid.uuid4()
print(user_id)          # например: 3e7c0e78-9a8e-4b52-9780-4e1c23f57abc
print(type(user_id))    # <class 'uuid.UUID'>
uuid4() генерирует UUID на основе криптографически стойкого генератора случайных чисел. Это самый популярный вариант. --- ### Разные версии UUID Модуль поддерживает несколько схем:
import uuid

# v1 — на основе MAC-адреса и времени
session_id = uuid.uuid1()

# v3 — хеш MD5 (детерминированный)
name_based_v3 = uuid.uuid3(uuid.NAMESPACE_DNS, "example.com")

# v5 — хеш SHA-1 (детерминированный, предпочтительнее v3)
name_based_v5 = uuid.uuid5(uuid.NAMESPACE_URL, "https://example.com")

print(session_id)
print(name_based_v3)
print(name_based_v5)
Где это полезно: - uuid4() — когда нужна просто уникальность. - uuid1() — когда порядок по времени важен (но может раскрывать MAC-адрес и время). - uuid3() / uuid5() — когда один и тот же «вход» должен давать один и тот же UUID (идентификатор по имени, URL и т.п.). --- ### UUID как строки и обратно Чаще всего UUID хранят как строки (например, в базе):
import uuid

order_id = uuid.uuid4()

order_str = str(order_id)    # в строку
print(order_str)

restored = uuid.UUID(order_str)  # обратно в объект UUID
print(restored == order_id)      # True
Можно работать и с байтами:
token = uuid.uuid4()
token_bytes = token.bytes
restored_token = uuid.UUID(bytes=token_bytes)
--- ### Практический мини-пример: генерация ключей для API
import uuid

def generate_api_key() -> str:
    return uuid.uuid4().hex  # без дефисов, 32 символа

keys = [generate_api_key() for _ in range(3)]
for k in keys:
    print(k)
Метод .hex возвращает компактное шестнадцатеричное представление, удобное для токенов. --- Модуль uuid снимает головную боль с уникальностью: вместо придумывания схем и «умных» счётчиков достаточно вызвать одну функцию — и получить идентификатор, с которым комфортно работать и локально, и в распределённых системах.

Модуль uuid: генерация уникальных идентификаторов
Модуль uuid: генерация уникальных идентификаторов

### Изучаем itertools: комбинации и декартовы произведения Если вы хоть раз вручную писали вложенные циклы, чтобы перебрать все пары, тройки или варианты, — модуль itertools создан для вас. Он умеет генерировать комбинации и продукты «на лету», не занимая лишнюю память. --- ## Комбинации: выбираем без повторов Комбинация — это выбор элементов без учета порядка. Из [1, 2, 3] пары такие: (1, 2), (1, 3), (2, 3).
from itertools import combinations

items = ['a', 'b', 'c', 'd']

for pair in combinations(items, 2):
    print(pair)
combinations(iterable, r): - не повторяет элементы; - (a, b) и (b, a) считаются одним вариантом; - всё генерируется лениво (по мере запроса). Полезно, когда перебираете возможные команды, наборы опций или подсеты признаков в задаче машинного обучения. --- ## Комбинации с повторениями Иногда элемент можно брать несколько раз: монеты, кубики, вкусы мороженого.
from itertools import combinations_with_replacement

flavors = ['vanilla', 'chocolate', 'strawberry']

for combo in combinations_with_replacement(flavors, 2):
    print(combo)
('vanilla', 'vanilla') здесь вполне допустим. --- ## Декартово произведение: все возможные варианты product — это как вложенные циклы, только в одну строку. Идеален для генерации всех комбинаций параметров.
from itertools import product

sizes = ['S', 'M', 'L']
colors = ['black', 'white']

for item in product(sizes, colors):
    print(item)
Результат: - ('S', 'black') - ('S', 'white') - ('M', 'black') - и т.д. Можно задать повторения:
for roll in product(range(1, 7), repeat=2):
    print(roll)
Это уже все возможные броски двух кубиков. --- ## Комбинации против продукта: что когда использовать? - Нужен выбор из одного набора без повторов → combinations - Нужен выбор из одного набора с возможными повторамиcombinations_with_replacement - Нужны все сочетания из нескольких наборовproduct - Хотите сымитировать несколько одинаковых наборов (кубики, пароли фиксированной длины) → product(..., repeat=n) --- itertools экономит память и время разработки: вы получаете мощный генератор вариантов без километров вложенных циклов. Как только ловите себя на мысли «сейчас напишу ещё один for внутри for», сначала загляните в этот модуль.

Изучаем модуль itertools: генерация комбинаций и продуктов
Изучаем модуль itertools: генерация комбинаций и продуктов

### Создание простого HTTP‑клиента с помощью http.client Большинство начинающих знакомятся с веб‑запросами через библиотеку requests. Удобно, но иногда полезно заглянуть на «низкий уровень» и понять, как все работает внутри. Для этого в стандартной библиотеке Python есть модуль http.client. --- ## Первый запрос: GET к публичному API Сделаем простой GET‑запрос к публичному API и разберём шаги.
import http.client
import json

conn = http.client.HTTPSConnection("jsonplaceholder.typicode.com")

conn.request("GET", "/posts/1")
response = conn.getresponse()

print("Status:", response.status, response.reason)

data = response.read()
text = data.decode("utf-8")
post = json.loads(text)

print("Title:", post["title"])

conn.close()
Что тут важно: - HTTPSConnection — шифрованное соединение (для http:// был бы HTTPConnection); - request(method, url, body=None, headers={}) — отправка запроса; - getresponse() — получение объекта ответа; - read() — сырые байты, которые обычно нужно декодировать (decode) и парсить (JSON, HTML и т.д.). --- ## Отправка данных: простой POST Теперь отправим JSON‑данные методом POST. Здесь уже нужны заголовки и тело запроса:
import http.client
import json

conn = http.client.HTTPSConnection("jsonplaceholder.typicode.com")

payload = {"title": "foo", "body": "bar", "userId": 1}
body = json.dumps(payload)

headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
}

conn.request("POST", "/posts", body=body, headers=headers)
response = conn.getresponse()

print("Status:", response.status)
print("Headers:", dict(response.getheaders()))

data = response.read().decode("utf-8")
created_post = json.loads(data)
print("Created ID:", created_post["id"])

conn.close()
Ключевые моменты: - Тело запроса мы сериализуем через json.dumps; - Обязательно указываем Content-Type; - getheaders() возвращает список пар заголовков. --- ## Обработка ошибок и таймаут Реальный клиент должен быть готов к сетевым ошибкам:
import http.client
import socket

try:
    conn = http.client.HTTPSConnection("example.com", timeout=3)
    conn.request("GET", "/")
    response = conn.getresponse()
    print("Status:", response.status)
    conn.close()
except (http.client.HTTPException, socket.timeout) as exc:
    print("Request failed:", exc)
Тут: - timeout защищает от «вечного ожидания»; - исключения HTTPException и socket.timeout помогают корректно отреагировать на проблемы сети. --- Зная http.client, легче понимать, что делает любая высокоуровневая библиотека: устанавливает соединение, формирует запрос, отправляет тело, читает статус, заголовки и содержимое. Это отличный шаг к более глубокому пониманию сетевого кода в Python.

Создание простого HTTP-клиента с помощью http.client
Создание простого HTTP-клиента с помощью http.client

Как использовать модуль statistics: среднее, медиана и мода без боли в мозге Когда вы считаете средние оценки, анализируете продажи или смотрите на результаты эксперимента — без базовой статистики далеко не уедешь. В Python для этого есть готовый инструмент — модуль statistics. Он избавляет от самописных циклов и формул и помогает считать всё в одну строку. --- ## Подготовка Модуль встроен в стандартную библиотеку, поэтому никаких установок не нужно:
import statistics as stats
Для примеров возьмём список чисел:
data = [10, 12, 13, 15, 15, 17, 18]
--- ## Среднее (mean) Среднее арифметическое — сумма всех значений, делённая на их количество. В statistics это делается так:
import statistics as stats

data = [10, 12, 13, 15, 15, 17, 18]
avg = stats.mean(data)
print(avg)  # 14.285714285714286
Круто то, что модуль сам обрабатывает int и float, не нужно думать о типах. --- ## Медиана (median) Медиана — это «середина» отсортированных данных. Она устойчивее к выбросам. Если в выборке внезапно появится «сумасшедшее» значение, среднее уедет, а медиана — почти нет.
import statistics as stats

data = [10, 12, 13, 1000]  # явный выброс
mean_value = stats.mean(data)
median_value = stats.median(data)

print(mean_value)   # 258.75
print(median_value) # 12.5
Медиана сразу показывает, что «нормальные» значения всё ещё где-то около 12–13, а не 258.75. --- ## Мода (mode) Мода — самое часто встречающееся значение. Полезно, когда нужно найти «типичный» элемент: самый популярный рейтинг, любимый номер, частый результат.
import statistics as stats

grades = [5, 4, 5, 3, 4, 5, 5, 4]
most_common = stats.mode(grades)
print(most_common)  # 5
Важно: если в данных несколько значений с одинаковой максимальной частотой, mode() в некоторых версиях Python может выбросить StatisticsError. Для таких случаев есть более гибкая функция multimode():
import statistics as stats

values = [1, 2, 2, 3, 3]
modes = stats.multimode(values)
print(modes)  # [2, 3]
--- ## Быстрый мини-анализ набора данных Соберём всё вместе:
import statistics as stats

data = [12, 15, 17, 15, 19, 21, 15, 18]

summary = {
    "mean": stats.mean(data),
    "median": stats.median(data),
    "mode": stats.mode(data),
    "min": min(data),
    "max": max(data)
}

print(summary)
# Пример вывода:
# {'mean': 16.5, 'median': 16.0, 'mode': 15, 'min': 12, 'max': 21}
Всего несколько строк — и вы уже видите «портрет» своих данных: где центр, где разброс и какое значение встречается чаще всего. Модуль statistics — отличный первый шаг в сторону анализа данных, без pandas и сложной математики. Идеален для учебных задач, прототипов и маленьких утилит.

Как использовать модуль statistics для вычислений: среднее, медиана и мода
Как использовать модуль statistics для вычислений: среднее, медиана и мода

Модуль pathlib: современная работа с файловой системой Если вы всё ещё склеиваете пути к файлам через os.path и плюсики — самое время познакомиться с pathlib. Это «современный» способ работы с путями в Python: объектно-ориентированный, удобный и кроссплатформенный. --- ### Зачем нужен pathlib pathlib решает несколько болезненных мест: - Не нужно думать о слэше: / под Linux и \ под Windows. - Пути становятся объектами, а не строками. - Методы для чтения/записи файлов встроены прямо в объект пути. Подключение модуля:
from pathlib import Path
--- ### Создание и комбинирование путей
from pathlib import Path

base_dir = Path.home()          # Домашняя папка пользователя
project_dir = base_dir / "my_project" / "data"  # Склеивание через /

print(project_dir)
print(project_dir.exists())     # Проверяем, существует ли путь
print(project_dir.is_dir())     # Это папка?
Оператор / перегружен и безопасно соединяет части пути с учётом ОС. --- ### Поиск файлов по маске Найдем все .txt в директории:
data_dir = Path("data")

for txt_file in data_dir.glob("*.txt"):
    print(txt_file.name, txt_file.stat().st_size, "bytes")
glob() поддерживает маски (*, **) и позволяет быстро обходить дерево каталогов. --- ### Чтение и запись файлов Необязательно открывать файл через open:
file_path = Path("data") / "notes.txt"

# Запись текста
file_path.write_text("Hello, pathlib!\nSecond line.")

# Чтение текста
content = file_path.read_text()
print(content)
Есть и бинарные варианты: write_bytes() и read_bytes(). --- ### Создание директорий и безопасная работа
logs_dir = Path("logs")
logs_dir.mkdir(exist_ok=True, parents=True)  # Создаст все недостающие папки

log_file = logs_dir / "app.log"

if not log_file.exists():
    log_file.write_text("Log start\n")
else:
    with log_file.open("a", encoding="utf-8") as f:
        f.write("New log line\n")
parents=True создаст всю цепочку директорий, а exist_ok=True не вызовет ошибку, если папка уже есть. --- ### Абсолютные пути и нормализация
p = Path("data") / ".." / "data" / "file.txt"
print(p)               # Относительный "кривой" путь
print(p.resolve())     # Нормализованный абсолютный путь
resolve() приводит путь к каноничному виду и показывает, куда он реально ведет. --- pathlib хорошо ложится на мышление «объект пути + действия с ним», избавляя от ручной возни со строками. Если вы начинаете с Python сегодня — имеет смысл сразу привыкать именно к такому стилю работы с файловой системой.

Модуль pathlib: современная работа с файловой системой
Модуль pathlib: современная работа с файловой системой