Перейти к содержимому
k07

k07. Файлы, JSON, CSV, YAML

k07. Файлы, JSON, CSV, YAML

UAV Python Pro · Конспект K07 · Модуль M1. Python intensive: файлы, JSON, CSV, YAML · Редакция 2.1 · языковая вычитка · полный код практик в аккордеоне · После CHECKPOINT-K06

До этого момента учебные программы в основном выводили результат на экран и не сохраняли его между запусками. В работе программиста беспилотных авиационных систем такое поведение почти неприемлемо. Телеметрию необходимо сохранять. Параметры соединения необходимо читать с диска. Траекторию полёта часто требуется представить в виде таблицы. На этом уроке вы научитесь записывать и читать текстовые файлы, освоите три распространённых формата данных — JSON, CSV и YAML — и разберёте аккуратный способ записи файла через промежуточный временный файл.

Ниже описано, как в этой редакции урока устроены примеры. В теоретических разделах каждый важный фрагмент кода приводится дважды. Сначала вы видите блок «код целиком»: в его начале указана только краткая цель, а далее следует сам код без подробных пояснений. Затем в раскрывающемся блоке аккордеона размещён тот же фрагмент с построчным разбором: после каждой смысловой строки добавлен комментарий преподавателя с префиксом «# →». В разделе практики для каждой задачи в аккордеоне вложен полный текст учебного файла. Этот текст совпадает с файлом в каталоге code/M01_python/. Если урок уже открыт на сайте, вы можете скопировать код прямо отсюда в проект PyCharm.

Главная мысль урока состоит в следующем. Файл на диске позволяет сохранить структуру данных между отдельными запусками программы. Формат JSON удобен в тех случаях, когда нужно сохранить вложенные объекты языка Python в виде, близком к их внутренней структуре. Формат CSV удобен, когда требуется таблица для электронных таблиц или для простого построения графика. Формат YAML удобен, когда файл конфигурации должен читать и изменять человек. Кодировку UTF-8 при работе с текстом следует указывать явно. Файлы, от которых зависит дальнейший запуск, предпочтительно сначала записывать под временным именем, а затем замещать ими целевой файл.

Как читать код на сайте. Знаки сравнения и угловые скобки в примерах могут отображаться полноширинными символами «<» и «>» — это защита публикации ZOO. В PyCharm вы набираете обычные клавиши языка Python. Файлы практики на диске проекта содержат нормальный синтаксис Python.

Содержание

1. Цели урока

После изучения этого урока вы будете уметь выполнять следующие действия.

  • Вы будете строить пути к файлам через pathlib.Path и проверять, существует ли файл.
  • Вы будете записывать и читать текстовые файлы в кодировке UTF-8.
  • Вы будете сохранять словарь Python в JSON и загружать его обратно.
  • Вы будете записывать последовательность отсчётов телеметрии в CSV и читать строки таблицы с преобразованием чисел.
  • Вы будете понимать назначение YAML для конфигураций и запускать учебный сценарий с YAML или с запасным JSON.
  • Вы будете выполнять аккуратную запись файла через временное имя и замену.
  • Вы будете выбирать формат под задачу: конфиг, обмен объектами, представлять траекторию полёта в виде таблицы.
Связь с разработкой программного обеспечения для БПЛА. Позднее ваши программы будут сохранять выгрузки данных, записывать CSV-файл траектории, читать конфигурацию с адресом симулятора SITL и с порогами безопасности. Без уверенной работы с файлами автоматизацию наземной станции управления и бортового компьютера-компаньона построить нельзя.
↑ К оглавлению

2. Словарь урока

ТерминПростыми словами
Файл Именованная область данных на диске, которую программа может создать, прочитать, изменить или удалить.
Путь Адрес файла или папки в файловой системе.
pathlib Стандартный модуль Python для работы с путями как с объектами.
Кодировка UTF-8 Способ представления текста в байтах; для кириллицы на курсе указываем её явно.
JSON Текстовый формат обмена структурированными данными; хорошо ложится на dict и list.
CSV Текстовая таблица: строки записей и столбцы полей, обычно через запятую.
YAML Текстовый формат конфигураций, удобный для чтения человеком.
Сериализация Превращение объектов программы в текст или байты для хранения и передачи.
Атомарная запись Приём: сначала писать во временный файл, затем заменить им целевой файл, чтобы не оставить «обрезанный» результат.
↑ К оглавлению

3. Зачем файлы программисту БПЛА

Программа наземной станции управления и программа бортового компьютера-компаньона обычно используются дольше одного запуска. После перезапуска им снова требуются адрес подключения, пороги безопасности и нередко последний маршрут. Кроме того, инженеру необходимо уметь сохранять результаты работы: таблицу высот, снимок параметров и отчёт о проверке.

Поэтому работа с файлами в курсе не является посторонней «офисной» темой. Она входит в тот же круг навыков, что и обмен сообщениями MAVLink. Сначала вы осваиваете запись и чтение на учебных данных. Затем вы перенесёте те же приёмы на журналы и файлы конфигурации симулятора и бортового комплекса.

ЗадачаТипичный формат
Сохранить один снимок телеметрии или набор параметров JSON
Сохранить траекторию полёта: время, высота и режим — по строкам таблицы CSV
Хранить конфиг соединения и порогов для человека YAML (иногда JSON)
Короткий человеческий отчёт Обычный .txt
↑ К оглавлению

4. Пути и pathlib

pathlib — это стандартный модуль Python, который представляет путь к файлу или папке объектом класса Path. Объект Path удобнее обычной текстовой строки: к нему можно безопасно добавлять части пути, проверять существование файла и читать текст.

Код целиком.

# Пример: путь к файлу рядом со скриптом
from pathlib import Path

here = Path(__file__).resolve().parent
out_dir = here / "k07_data"
out_dir.mkdir(exist_ok=True)
note_path = out_dir / "hello_note.txt"
print(note_path)
print("exists before write:", note_path.exists())
  • Построчный разбор

    Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с последовательности символов «# →», содержат комментарий преподавателя. Эти строки не являются обязательной частью программы, которую вы будете сдавать как готовый производственный код.

    from pathlib import Path
    # → Подключаем стандартный модуль для работы с путями как с объектами.
    
    here = Path(__file__).resolve().parent
    # → Path(__file__) — путь к текущему .py-файлу. resolve() делает путь полным. parent — каталог, где лежит файл.
    
    out_dir = here / "k07_data"
    # → Оператор «/» у Path добавляет часть пути. Получается папка k07_data рядом со скриптом.
    
    out_dir.mkdir(exist_ok=True)
    # → Создаём папку. Если она уже есть, ошибки не будет.
    
    note_path = out_dir / "hello_note.txt"
    # → Собираем полный путь к файлу hello_note.txt внутри этой папки.
    
    print(note_path)
    # → Печатаем путь, чтобы увидеть, куда программа будет писать.
    
    print("exists before write:", note_path.exists())
    # → exists() отвечает True/False: есть ли уже такой файл на диске.
Зачем нужен Path(__file__). Учебный скрипт может запускаться из разных рабочих каталогов. Привязка к расположению самого файла делает путь предсказуемым: данные появятся рядом со скриптом, а не в случайном каталоге, из которого программу запустили.
Оператор «/» у Path. Для объектов Path выражение path / "name.txt" означает «добавить часть пути», а не деление чисел. Это сделано нарочно, чтобы код читался как маршрут в файловой системе.
↑ К оглавлению

5. Текстовые файлы и UTF-8

Кодировка UTF-8 — это согласованный способ представлять символы текста, включая кириллицу, в виде байтов на диске. На курсе при работе с текстом мы указываем encoding="utf-8" явно. Тогда поведение программы на системе Windows становится предсказуемым.

Код целиком.

# Пример: запись и чтение текста в UTF-8
from pathlib import Path

path = Path("k07_data") / "hello_note.txt"
path.parent.mkdir(exist_ok=True)
path.write_text("Строка один\nСтрока два\n", encoding="utf-8")
text = path.read_text(encoding="utf-8")
print(text)
  • Построчный разбор

    Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с последовательности символов «# →», содержат комментарий преподавателя. Эти строки не являются обязательной частью программы, которую вы будете сдавать как готовый производственный код.

    from pathlib import Path
    # → Снова импортируем Path, чтобы указать путь к текстовому файлу.
    
    path = Path("k07_data") / "hello_note.txt"
    # → Путь относительно текущей рабочей папки запуска (в практике task1 путь привязан к __file__).
    
    path.parent.mkdir(exist_ok=True)
    # → parent — каталог файла; создаём его при необходимости.
    
    path.write_text("Строка один\nСтрока два\n", encoding="utf-8")
    # → write_text записывает весь текст целиком. encoding="utf-8" сохраняет кириллицу предсказуемо.
    
    text = path.read_text(encoding="utf-8")
    # → read_text читает весь файл в одну строку Python.
    
    print(text)
    # → Печатаем прочитанное, чтобы убедиться, что запись удалась.
Без явной кодировки. Если encoding не указать, Python может выбрать локальную кодировку среды. Тогда кириллица иногда превращается в нечитаемые символы или вызывает ошибку. Для курса правило простое: текстовые файлы открываем и пишем с UTF-8.

Контекстный менеджер with гарантирует закрытие файла даже при ошибке внутри блока. Для CSV он особенно привычен.

Код целиком.

# Пример: with гарантирует закрытие файла
from pathlib import Path

path = Path("k07_data") / "lines.txt"
with path.open("w", encoding="utf-8") as f:
    f.write("first\n")
    f.write("second\n")
  • Построчный разбор

    Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с последовательности символов «# →», содержат комментарий преподавателя. Эти строки не являются обязательной частью программы, которую вы будете сдавать как готовый производственный код.

    from pathlib import Path
    # → Импортируем класс Path из модуля pathlib.
    
    path = Path("k07_data") / "lines.txt"
    # → Задаём путь к файлу, который будет создан.
    
    with path.open("w", encoding="utf-8") as f:
    # → open открывает файл на запись («w»). with закроет файл даже при ошибке внутри блока.
    
        f.write("first\n")
    # → Пишем первую строку. \n — перевод строки.
    
        f.write("second\n")
    # → Пишем вторую строку.
    
    # здесь файл уже закрыт
    # → После выхода из with дескриптор файла закрыт автоматически.
↑ К оглавлению

6. Формат JSON

JSON (JavaScript Object Notation) — текстовый формат хранения и обмена структурированными данными. Для Python он особенно удобен: словарь dict и список list переводятся в JSON почти напрямую.

Сериализация — это превращение объектов программы в текст или байты, чтобы их можно было сохранить или передать. Обратный процесс называют десериализацией: текст снова становится объектами Python.

Код целиком.

# Пример: dict → JSON → dict
import json
from pathlib import Path

sample = {
    "mode": "GUIDED",
    "altitude_m": 12.5,
    "link_ok": True,
}
path = Path("k07_data") / "telemetry_sample.json"
text = json.dumps(sample, ensure_ascii=False, indent=2)
path.write_text(text, encoding="utf-8")
loaded = json.loads(path.read_text(encoding="utf-8"))
print(loaded["mode"], loaded["altitude_m"])
  • Построчный разбор

    Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с последовательности символов «# →», содержат комментарий преподавателя. Эти строки не являются обязательной частью программы, которую вы будете сдавать как готовый производственный код.

    import json
    # → Стандартный модуль для JSON.
    
    from pathlib import Path
    # → Импортируем pathlib для указания пути к файлу.
    
    sample = {
    # → Обычный словарь Python — учебный снимок телеметрии.
    
        "mode": "GUIDED",
    # → Ключ mode — строка с режимом.
    
        "altitude_m": 12.5,
    # → Высота числом float.
    
        "link_ok": True,
    # → Логический флаг.
    
    }
    # → Конец словаря.
    
    path = Path("k07_data") / "telemetry_sample.json"
    # → Куда сохраним JSON-файл.
    
    text = json.dumps(sample, ensure_ascii=False, indent=2)
    # → dumps: объект Python → текст JSON. ensure_ascii=False оставляет кириллицу читаемой. indent=2 делает отступы.
    
    path.write_text(text, encoding="utf-8")
    # → Записываем JSON-текст на диск.
    
    loaded = json.loads(path.read_text(encoding="utf-8"))
    # → loads: текст JSON → снова dict/list в Python.
    
    print(loaded["mode"], loaded["altitude_m"])
    # → Проверяем, что поля на месте после круглого пути запись→чтение.
Параметр dumpsЗачем
ensure_ascii=False Кириллические символы остаются читаемыми в файле
indent=2 Удобные для чтения отступы; файл проще просматривать человеку
Что JSON не умеет «как Python». В JSON нет кортежа tuple и нет отдельного типа set. Ключи объекта JSON — строки. Булевы значения в тексте выглядят как true/false, но json.loads вернёт True/False Python. Для учебного курса этого достаточно.
Связь с практикой. Полный файл k07_task2_json_telemetry.py вложен в аккордеон задачи 7.2 ниже.
↑ К оглавлению

7. Формат CSV

CSV (Comma-Separated Values) — текстовый табличный формат. Каждая строка файла — запись. Поля внутри строки обычно разделены запятой. Такой файл легко открыть в Excel и так же легко дописать построчно из программы.

Для последовательности отсчётов телеметрии формат CSV естественен: одна строка соответствует одному моменту времени, а столбцы содержат поля t_s, alt_m, mode и другие величины.

Код целиком.

# Пример: list[dict] → CSV → чтение с float()
import csv
from pathlib import Path

path = Path("k07_data") / "track_demo.csv"
rows = [
    {"t_s": 0.0, "alt_m": 0.5, "mode": "STABILIZE"},
    {"t_s": 1.0, "alt_m": 5.0, "mode": "GUIDED"},
]
fieldnames = ["t_s", "alt_m", "mode"]

with path.open("w", encoding="utf-8", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    for row in rows:
        writer.writerow(row)

with path.open("r", encoding="utf-8", newline="") as f:
    reader = csv.DictReader(f)
    for row in reader:
        alt_m = float(row["alt_m"])
        print(row["mode"], alt_m)
  • Построчный разбор

    Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с последовательности символов «# →», содержат комментарий преподавателя. Эти строки не являются обязательной частью программы, которую вы будете сдавать как готовый производственный код.

    import csv
    # → Стандартный модуль таблиц CSV.
    
    from pathlib import Path
    # → Импортируем pathlib для построения путей к файлам.
    
    path = Path("k07_data") / "track_demo.csv"
    # → Задаём путь к CSV-файлу таблицы.
    
    rows = [
    # → Список словарей — одна строка таблицы = один dict.
    
        {"t_s": 0.0, "alt_m": 0.5, "mode": "STABILIZE"},
    # → Первая точка траектории.
    
        {"t_s": 1.0, "alt_m": 5.0, "mode": "GUIDED"},
    # → Вторая точка траектории.
    
    ]
    # → Конец списка.
    
    fieldnames = ["t_s", "alt_m", "mode"]
    # → Порядок и имена столбцов.
    
    with path.open("w", encoding="utf-8", newline="") as f:
    # → Открываем на запись. newline="" — рекомендация для csv на Windows.
    
        writer = csv.DictWriter(f, fieldnames=fieldnames)
    # → Писатель, который понимает dict.
    
        writer.writeheader()
    # → Первая строка файла — заголовки столбцов.
    
        for row in rows:
    # → Для каждой записи…
    
            writer.writerow(row)
    # → …пишем строку таблицы.
    
    with path.open("r", encoding="utf-8", newline="") as f:
    # → Открываем тот же файл на чтение.
    
        reader = csv.DictReader(f)
    # → Читатель отдаёт каждую строку как dict.
    
        for row in reader:
    # → Обходим строки данных.
    
            alt_m = float(row["alt_m"])
    # → Важно: из CSV числа приходят строками. float() делает число.
    
            print(row["mode"], alt_m)
    # → Печатаем режим и высоту.
Типы при чтении CSV. Модуль csv не угадывает, что поле «12.5» — это float. Он отдаёт строки. Если нужно считать среднее высоты, сначала выполните float(...). Иначе арифметика будет ошибочной или программа завершится ошибкой типа.
Связь с практикой. Полный файл k07_task3_csv_track.py — в задаче 7.3.
↑ К оглавлению

8. Формат YAML

YAML — текстовый формат, который часто используют для конфигурационных файлов. Его удобно читать глазами: структура задаётся отступами, меньше служебных скобок, чем в JSON.

Типичный учебный файл конфигурации наземного сценария может хранить тип соединения, сетевой адрес, номер порта и пороги безопасности. Оператор открывает файл, изменяет порт симулятора SITL и запускает программу снова.

Пример внешнего вида файла конфигурации (это текст YAML, а не программа Python):

# Пример вида app_config.yaml (учебный текст, не Python)
connection:
  type: udp
  address: 127.0.0.1
  port: 14550
safety:
  min_voltage_v: 14.0
  require_operator_ack: true
Библиотека PyYAML. В стандартной библиотеке Python нет полноценного разбора YAML «из коробки». Для настоящей работы с YAML обычно ставят пакет PyYAML командой python -m pip install pyyaml внутри venv. На K07 практика устроена мягко: если PyYAML ещё не установлен, скрипт задачи 7.4 сохранит тот же смысл в JSON и подскажет команду установки на потом. Принудительно ставить пакет прямо сейчас не требуется.
Безопасность загрузки YAML. Если будете пользоваться PyYAML, для ненадёжных файлов используйте safe_load. Учебные конфиги курса мы пишем сами; привычку safe_load всё равно стоит закрепить.
Связь с практикой. Полный файл k07_task4_yaml_config.py — в задаче 7.4 (оба пути: YAML и JSON-запасной).
↑ К оглавлению

9. Аккуратная запись через временный файл

Атомарная запись в учебном смысле — это приём, при котором программа сначала записывает данные во временный файл, а затем заменяет им целевой файл. Если запись оборвётся на середине, старый целевой файл с большей вероятностью останется целым, а «обрезанным» окажется только временный.

Код целиком.

# Пример: запись через временный файл и os.replace
from pathlib import Path
import json
import os

final_path = Path("k07_data") / "settings.json"
tmp_path = Path("k07_data") / "settings.json.tmp"
final_path.parent.mkdir(exist_ok=True)

payload = json.dumps({"version": 1, "ok": True}, ensure_ascii=False, indent=2)
tmp_path.write_text(payload, encoding="utf-8")
os.replace(tmp_path, final_path)
  • Построчный разбор

    Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с последовательности символов «# →», содержат комментарий преподавателя. Эти строки не являются обязательной частью программы, которую вы будете сдавать как готовый производственный код.

    from pathlib import Path
    # → Импортируем pathlib для построения путей к файлам.
    
    import json
    # → Импортируем json, чтобы сформировать учебный JSON-текст.
    
    import os
    # → Импортируем os: функция os.replace заместит целевой файл временным.
    
    final_path = Path("k07_data") / "settings.json"
    # → Итоговое имя, которое должна видеть программа.
    
    tmp_path = Path("k07_data") / "settings.json.tmp"
    # → Временное имя на время записи.
    
    final_path.parent.mkdir(exist_ok=True)
    # → Каталог должен существовать.
    
    payload = json.dumps({"version": 1, "ok": True}, ensure_ascii=False, indent=2)
    # → Готовим полное содержимое в памяти.
    
    tmp_path.write_text(payload, encoding="utf-8")
    # → Шаг 1: полностью записываем временный файл.
    
    os.replace(tmp_path, final_path)
    # → Шаг 2: заменяем целевой файл готовым временным. Так меньше шанс «обрезанного» конфига.
Насколько это «атомарно». На разных операционных системах гарантии чуть отличаются. Для курса достаточно идеи: не писать критичный конфиг поверх себя одним долгим потоком, если можно сначала закончить временный файл. Для логов телеметрии иногда важнее непрерывная дописка; для конфигов — как раз замена целиком.
Связь с практикой. Полный файл k07_task5_atomic_write.py — в задаче 7.5.
↑ К оглавлению

10. Какой формат выбрать

ВопросЕсли ответ утвердительный, предпочтителен формат
Требуется сохранить вложенный объект, например словарь внутри словаря, а затем снова получить его в программе на Python? JSON
Требуется таблица по времени для работы в табличном редакторе или для простого анализа столбцов? CSV
Файл конфигурации должен изменять человек в текстовом редакторе, и важна удобочитаемость? YAML (или аккуратный JSON)
Требуется короткий текстовый журнал для просмотра человеком без строгой структуры? Обычный текстовый файл
Практическое правило курса. Оно состоит в следующем. Для снимка телеметрии и для обмена структурированными данными между своими программами обычно выбирают JSON. Для траектории полёта и для рядов чисел по времени обычно выбирают CSV. Для подробного файла конфигурации, который читает человек, при наличии библиотеки обычно выбирают YAML. Секреты, пароли и ключи доступа не следует помещать в открытые учебные файлы репозитория.
↑ К оглавлению

11. Типичные ошибки

ОшибкаЧто происходитКак думать
Забыли encoding="utf-8" Нарушается отображение кириллицы Для текстовых файлов курса всегда указывайте кодировку UTF-8
Читаете CSV и складываете строки как числа Получается неверный результат либо возникает ошибка типа Сначала преобразуйте значение через float(...) или int(...)
Пишете JSON вручную «как текст» с ошибкой запятой Вызов json.loads завершается ошибкой Соберите объект Python и выполните сериализацию через dumps
Открыли файл и забыли закрыть в длинной программе Возможны утечки дескрипторов и блокировки файла Открывайте файл с помощью оператора with
Пишете конфиг поверх себя и обрыв процесса На диске остаётся неполностью записанный файл Сначала запишите временный файл, затем выполните os.replace
Кладёте секреты в Git Возможна утечка конфиденциальных данных Исключайте секреты через .gitignore и храните их вне репозитория
↑ К оглавлению

12. Практика (полный код в аккордеоне)

В каждой карточке задачи ниже в раскрывающемся блоке приведён полный текст соответствующего учебного файла. Этот текст совпадает с файлами в каталоге code/M01_python/. Рекомендуется такой порядок работы. Сначала внимательно прочитайте условие. Затем раскройте аккордеон, скопируйте код в файл проекта под тем же именем, запустите его в виртуальном окружении и сверьте вывод с ожидаемым результатом. Каталог k07_data/ учебные сценарии создадут самостоятельно.

Задача 7.1. Path, запись и чтение текста

Файл практики: k07_task1_path_write_read.py.

Создайте текстовый файл через pathlib, запишите две строки, прочитайте файл обратно, напечатайте признак существования и размер в байтах.

Ожидаемый результат. В консоли путь к hello_note.txt, текст из двух строк, exists: True и ненулевой size_bytes.

  • Полный текст файла k07_task1_path_write_read.py

    Скопируйте приведённое ниже содержимое в файл code/M01_python/k07_task1_path_write_read.py в проекте PyCharm (venv) и запустите. Ниже приведён полный учебный текст файла с комментариями.

    # k07_task1_path_write_read.py
    # Цель: pathlib.Path — путь к файлу; запись и чтение текста в UTF-8.
    # Запуск: из venv проекта. Файл появится рядом, в k07_data/.
    
    from pathlib import Path
    
    # Папка рядом с этим скриптом
    HERE = Path(__file__).resolve().parent
    OUT_DIR = HERE / "k07_data"
    OUT_DIR.mkdir(exist_ok=True)
    
    # Path — объект пути, а не просто строка
    note_path = OUT_DIR / "hello_note.txt"
    
    # Пишем текст. encoding="utf-8" — чтобы кириллица не ломалась.
    text = "UAV Python Pro\nK07: первая запись в файл\n"
    note_path.write_text(text, encoding="utf-8")
    print("written:", note_path)
    
    # Читаем обратно
    loaded = note_path.read_text(encoding="utf-8")
    print("loaded:")
    print(loaded)
    print("exists:", note_path.exists())
    print("size_bytes:", note_path.stat().st_size)

Задача 7.2. JSON телеметрии

Файл практики: k07_task2_json_telemetry.py.

Сохраните учебный словарь телеметрии в JSON с ensure_ascii=False и indent=2, затем загрузите файл и напечатайте mode и altitude_m.

Ожидаемый результат. Файл telemetry_sample.json в k07_data; в консоли mode GUIDED и высота 12.5.

  • Полный текст файла k07_task2_json_telemetry.py

    Скопируйте приведённое ниже содержимое в файл code/M01_python/k07_task2_json_telemetry.py в проекте PyCharm (venv) и запустите. Ниже приведён полный учебный текст файла с комментариями.

    # k07_task2_json_telemetry.py
    # Цель: сохранить dict/list в JSON и прочитать обратно.
    # JSON удобен для обмена структурированными данными между программами.
    
    import json
    from pathlib import Path
    
    HERE = Path(__file__).resolve().parent
    OUT = HERE / "k07_data"
    OUT.mkdir(exist_ok=True)
    path = OUT / "telemetry_sample.json"
    
    # Учебный снимок телеметрии (не MAVLink-кадр)
    sample = {
        "vehicle": "sitl-copter-demo",
        "mode": "GUIDED",
        "altitude_m": 12.5,
        "battery_v": 15.8,
        "satellites": 14,
        "link_ok": True,
    }
    
    # ensure_ascii=False — кириллица в файле читается как текст, не \uXXXX
    # indent=2 — красивые отступы для человека
    path.write_text(
        json.dumps(sample, ensure_ascii=False, indent=2),
        encoding="utf-8",
    )
    print("json written:", path)
    
    # loads/load: из текста/файла обратно в объекты Python
    raw = path.read_text(encoding="utf-8")
    loaded = json.loads(raw)
    print("mode from file:", loaded["mode"])
    print("altitude_m from file:", loaded["altitude_m"])

Задача 7.3. CSV-файл траектории

Файл практики: k07_task3_csv_track.py.

Запишите три строки траектории через csv.DictWriter и прочитайте их DictReader с преобразованием t_s и alt_m через float.

Ожидаемый результат. Файл track_demo.csv; три строки печати t=… alt=… mode=…

  • Полный текст файла k07_task3_csv_track.py

    Скопируйте приведённое ниже содержимое в файл code/M01_python/k07_task3_csv_track.py в проекте PyCharm (venv) и запустите. Ниже приведён полный учебный текст файла с комментариями.

    # k07_task3_csv_track.py
    # Цель: записать последовательность отсчётов телеметрии в CSV и прочитать строки обратно.
    # CSV удобен для Excel и простых графиков.
    
    import csv
    from pathlib import Path
    
    HERE = Path(__file__).resolve().parent
    OUT = HERE / "k07_data"
    OUT.mkdir(exist_ok=True)
    path = OUT / "track_demo.csv"
    
    # list[dict] — формат, который вы уже знаете с K04/K05
    rows = [
        {"t_s": 0.0, "alt_m": 0.5, "mode": "STABILIZE"},
        {"t_s": 1.0, "alt_m": 5.0, "mode": "GUIDED"},
        {"t_s": 2.0, "alt_m": 12.0, "mode": "AUTO"},
    ]
    
    fieldnames = ["t_s", "alt_m", "mode"]
    
    # newline="" — рекомендация документации csv на Windows
    with path.open("w", encoding="utf-8", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        for row in rows:
            writer.writerow(row)
    
    print("csv written:", path)
    
    # Чтение
    with path.open("r", encoding="utf-8", newline="") as f:
        reader = csv.DictReader(f)
        for row in reader:
            # значения из CSV приходят строками — для чисел нужен float()
            t_s = float(row["t_s"])
            alt_m = float(row["alt_m"])
            mode = row["mode"]
            print(f"t={t_s} alt={alt_m} mode={mode}")

Задача 7.4. YAML-конфиг или JSON-запасной путь

Файл практики: k07_task4_yaml_config.py.

Запустите скрипт. Если установлен PyYAML, будет записан app_config.yaml. Если нет — app_config_fallback.json и подсказка про pip install pyyaml. В обоих случаях напечатайте порт 14550 из загруженной структуры.

Ожидаемый результат. В консоли udp port: 14550 и сообщение о YAML либо о JSON fallback.

  • Полный текст файла k07_task4_yaml_config.py

    Скопируйте приведённое ниже содержимое в файл code/M01_python/k07_task4_yaml_config.py в проекте PyCharm (venv) и запустите. Ниже приведён полный учебный текст файла с комментариями.

    # k07_task4_yaml_config.py
    # Цель: понять YAML как человекочитаемый конфиг.
    # PyYAML может быть не установлен — поэтому есть два режима:
    #   1) если import yaml работает — пишем и читаем .yaml
    #   2) иначе — сохраняем тот же смысл в .json и объясняем, что поставить позже
    
    from pathlib import Path
    import json
    
    HERE = Path(__file__).resolve().parent
    OUT = HERE / "k07_data"
    OUT.mkdir(exist_ok=True)
    
    config = {
        "connection": {
            "type": "udp",
            "address": "127.0.0.1",
            "port": 14550,
        },
        "safety": {
            "min_voltage_v": 14.0,
            "require_operator_ack": True,
        },
        "mission": {
            "default_alt_m": 40.0,
        },
    }
    
    try:
        import yaml  # type: ignore
    except ImportError:
        yaml = None
    
    if yaml is not None:
        path = OUT / "app_config.yaml"
        # allow_unicode=True — кириллица, если появится в конфиге
        text = yaml.safe_dump(config, allow_unicode=True, sort_keys=False)
        path.write_text(text, encoding="utf-8")
        loaded = yaml.safe_load(path.read_text(encoding="utf-8"))
        print("yaml written:", path)
        print("udp port:", loaded["connection"]["port"])
    else:
        # Запасной путь без дополнительной установки пакета
        path = OUT / "app_config_fallback.json"
        path.write_text(json.dumps(config, ensure_ascii=False, indent=2), encoding="utf-8")
        print("PyYAML not installed — wrote JSON fallback:", path)
        print("Later (when mentor says): python -m pip install pyyaml")
        loaded = json.loads(path.read_text(encoding="utf-8"))
        print("udp port:", loaded["connection"]["port"])

Задача 7.5. Запись через временный файл

Файл практики: k07_task5_atomic_write.py.

Запишите JSON во временный файл settings.json.tmp, затем os.replace в settings.json, прочитайте итоговый файл.

Ожидаемый результат. Файл settings.json; в консоли final: … и JSON с version и ok.

  • Полный текст файла k07_task5_atomic_write.py

    Скопируйте приведённое ниже содержимое в файл code/M01_python/k07_task5_atomic_write.py в проекте PyCharm (venv) и запустите. Ниже приведён полный учебный текст файла с комментариями.

    # k07_task5_atomic_write.py
    # Цель: «атомарная» запись — сначала временный файл, потом replace.
    # Так меньше шанс получить полузаписанный конфиг при сбое.
    
    from pathlib import Path
    import json
    import os
    
    HERE = Path(__file__).resolve().parent
    OUT = HERE / "k07_data"
    OUT.mkdir(exist_ok=True)
    
    final_path = OUT / "settings.json"
    tmp_path = OUT / "settings.json.tmp"
    
    data = {"version": 1, "vehicle": "demo", "ok": True}
    payload = json.dumps(data, ensure_ascii=False, indent=2)
    
    # 1) пишем во временный файл
    tmp_path.write_text(payload, encoding="utf-8")
    # 2) атомарно (насколько позволяет ОС) заменяем целевой файл
    os.replace(tmp_path, final_path)
    
    print("final:", final_path)
    print("content:", final_path.read_text(encoding="utf-8"))

Задача 7.6. Git commit

Сохраните результаты практики в истории Git. Каталог k07_data/ с выходными учебными файлами в репозиторий обычно не включают. При необходимости добавьте строку k07_data/ в файл .gitignore.

git add code/M01_python/k07_*.py
git commit -m "K07: pathlib json csv yaml atomic write practice"
↑ К оглавлению

13. Проверьте себя

  1. Чем объект Path удобнее строки с путём?
  2. Почему на курсе для текста мы указываем encoding="utf-8"?
  3. Чем JSON удобен для dict и list?
  4. Почему после csv.DictReader числовые поля нужно преобразовывать через float или int?
  5. Для какой задачи YAML обычно предпочтительнее «плотного» JSON?
  6. Что делает os.replace после записи временного файла и зачем так делают?
  7. Какой формат выберете для траектории высоты по времени и почему?
  8. Почему секреты не следует класть в учебный репозиторий?
  9. Зачем в уроке два представления одного и того же кода: «целиком» и «построчный разбор»?
↑ К оглавлению

14. Чек-лист самопроверки

  • Строю пути через pathlib.Path и создаю папку exist_ok=True
  • Пишу и читаю текст в UTF-8
  • Сохраняю и загружаю JSON
  • Пишу и читаю CSV, преобразую числа при чтении
  • Понимаю роль YAML для конфигов и запасной путь без PyYAML
  • Выполняю запись через временный файл и replace
  • Могу выбрать JSON / CSV / YAML под задачу
  • Открыл аккордеоны практики и сверил полный код с запуском в виртуальном окружении
  • Запускал k07_task1 … k07_task5 в venv
  • Сделал git commit по K07
Результат. Если вы уже присылали «K07 чек-лист закрыт» по редакции 1.0, достаточно внимательно просмотреть аккордеоны редакции 2.1 и убедиться, что полный код на сайте совпадает с вашими файлами. Если что-то расходится — скопируйте версию из аккордеона. Далее по плану: K08 · Исключения, логирование и отладка.
↑ К оглавлению

15. Что дальше

  • Предыдущий урок: K06 · Модули, пакеты, venv, pip
  • Текущий урок: K07 · Файлы, JSON, CSV, YAML (редакция 2.0, новый шаблон кода)
  • Следующий урок: K08 · Исключения, logging, отладка (будет переведён на тот же шаблон)
  • Горизонт модуля M1: K12 и гейт G1
Шаблон вёрстки с K07 r2. 1) Теория: блок pre «код целиком» + аккордеон «построчный разбор». 2) Практика: аккордеон с полным текстом kNN_task*.py. 3) Голос преподавателя, uk-marker, ZOO-safe. Спека: docs/06_ZOO_PUBLISH_SPEC.md.
↑ К оглавлению

 

 

Вторник, 18 августа 2026
k07. Файлы, JSON, CSV, YAML