Глава 15 · Python и файлы

Безопасная работа с файлами

Запись в файл — действие с последствиями. Разумная осторожность стоит нескольких лишних строк кода.

Перед записью — спросите себя

  1. Я хочу заменить содержимое или дозаписать?
  2. Или создать только если файла ещё нет?
  3. Нужна ли резервная копия перед изменением?
  4. Это точно тот путь, который я имею в виду?
Режим — осознанный выбор, не заготовка из примера
"r", "w", "a", "x" — каждый несёт разные последствия для существующего содержимого (раздел 15.15). Копировать режим из примера без вопроса «а что именно мне сейчас нужно?» — источник потери данных.

Практики этого курса — только в песочнице

Обязательное правило безопасности практик
Все файловые практики этого курса читают, создают, изменяют и удаляют файлы только внутри собственной рабочей папки практики. Никогда — файлы репозитория курса, документы, которые вы уже сохранили, домашнюю папку пользователя или произвольные абсолютные пути. Это касается и ваших собственных программ: не выполняйте разрушительные операции над путём, который пришёл откуда-то извне, не проверив его.

Путь наружу из своей папки — path traversal

put_naruzhu.py
imya_fajla = input("Имя файла для сохранения: ")
# если пользователь введёт "../../important.txt" — куда это укажет?
path = Path("data") / imya_fajla
Наивное объединение пути — риск выйти за пределы папки
Если имя файла приходит от пользователя и содержит .., простое объединение с базовой папкой может увести путь за её пределы, в совершенно другое место файловой системы. Мы не строим здесь полноценный фильтр безопасности — важно просто знать о самом риске и не доверять пользовательскому имени файла как безопасному само по себе.

Профессиональнее: безопасное сохранение

Идея, снижающая риск оставить основной файл в повреждённом промежуточном состоянии: сначала записать во временный файл в той же папке, а затем заменить основной файл целиком:

bezopasnoe_sohranenie.py
import json
from pathlib import Path

def bezopasno_sohranit(path: Path, data: dict) -> None:
    vremenny = path.with_suffix(path.suffix + ".tmp")
    with vremenny.open("w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)
    vremenny.replace(path)   # заменяем основной файл только когда всё уже записано
Не абсолютная гарантия — а сниженный риск
Это снижает риск оставить path в частично записанном состоянии при обрыве записи, но не является абсолютной гарантией сохранности данных на любом оборудовании и любой файловой системе.

Резервная копия перед заменой

backup_pered_zamenoj.py
import shutil
from pathlib import Path

path = Path("save.json")
if path.exists():
    shutil.copy(path, path.with_suffix(".json.bak"))
Одна резервная копия, не бесконечная цепочка
Одной актуальной резервной копии перед заменой обычно достаточно для учебных и небольших прикладных задач — не нужно плодить бесконечно растущую цепочку файлов .bak.bak.bak.
Практика: безопасная запись и резервная копия
Интерактивный ноутбук прямо в браузере — Python 3.14 через Pyodide, без установки
Открыть практику →