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

k04. Коллекции list, tuple, dict, set

k04. Коллекции list, tuple, dict, set

UAV Python Pro · Конспект K04 · Модуль M1. Python intensive: коллекции list, tuple, dict, set · Редакция 1.2 · правка ученика + QA · После CHECKPOINT-K03

На прошлом уроке вы научились хранить отдельные значения параметров телеметрии: одну высоту, один режим, один флаг связи. В реальной работе с БПЛА значения почти всегда поступают группами: список точек маршрута, словарь параметров автопилота, набор уникальных режимов из лога, очередь последних отсчётов телеметрии. На этом уроке вы разберёте четыре встроенные коллекции Python — list, tuple, dict и set — и поймёте, какую из них выбирать под задачу БПЛА. Практика выполняется в виртуальном окружении проекта из K01. Готовые файлы находятся в папке code/M01_python/.

Главная мысль урока: коллекция — это способ держать много связанных значений под одним именем и обходить их циклом. Список (list) удобен, когда важен порядок и нужны изменения. Кортеж (tuple) — когда набор координат «зафиксирован» и его не должны случайно изменить. Словарь (dict) — когда доступ идёт по имени, как к параметру RTL_ALT. Множество (set) — когда важны уникальные элементы, например какие режимы вообще встречались в логе.

Содержание

1. Цели урока

После этого урока вы будете уметь своими словами и в коде:

  • создавать списки, читать элементы по индексу, брать срезы, добавлять элементы;
  • объяснить, чем кортеж отличается от списка, и когда кортеж уместен для точки маршрута;
  • создавать словарь параметров, читать значение по ключу, безопасно использовать метод get, обновлять значение;
  • собирать множество уникальных режимов из «лога» и проверять вхождение элемента;
  • выбрать подходящую коллекцию под задачу (маршрут, параметры, уникальные режимы, пачка телеметрии);
  • написать простой list comprehension для фильтрации учебной телеметрии;
Связь с дронами: миссия — это по сути список точек. Параметры ArduPilot — словарь «имя → значение». Разбор лога режимов — множество уникальных строк. Почти весь MAVLink-код позже будет строиться вокруг этих четырёх идей.
↑ К оглавлению

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

ТерминПростыми словами
Коллекция Объект, который хранит несколько значений и умеет их отдавать по одному (часто в цикле).
list (список) Упорядоченная изменяемая последовательность. Квадратные скобки: [1, 2, 3].
tuple (кортеж) Упорядоченная, но неизменяемая последовательность. Круглые скобки: (lat, lon, alt).
dict (словарь) Набор пар «ключ → значение». Фигурные скобки: {"RTL_ALT": 5000}.
set (множество) Набор уникальных элементов; на порядок элементов при печати для маршрута опираться нельзя. Фигурные скобки: {"AUTO", "RTL"}.
Индекс Номер позиции в списке или кортеже. В Python нумерация с нуля: первый элемент имеет индекс 0.
Срез (slice) Кусок последовательности: modes[2:4] — элементы с индекса 2 включительно по 4 не включая.
Ключ (key) Имя, по которому словарь находит значение. Часто строка: "BATT_LOW_VOLT".
Итерировать Обходить элементы по одному, обычно циклом for.
list comprehension Короткая запись создания нового списка из старого с фильтром или преобразованием.
Mutable / immutable Изменяемый / неизменяемый объект. Список меняют на месте; кортеж после создания не изменяют.
↑ К оглавлению

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

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

ЗадачаУдобная коллекцияПочему
Порядок режимов полёта, которые вы перебираете в меню list Важен порядок, список можно дополнять
Одна точка: широта, долгота, высота tuple Фиксированный набор полей, случайно «не дописать» четвёртое число сложнее
Параметры автопилота по имени dict Набор пар «имя → значение»: обращаетесь по имени, а не по номеру. Пример: params["RTL_ALT"] сразу даёт нужное значение.
Какие режимы встретились в логе хотя бы раз set Дубликаты исчезают сами
Пачка отсчётов телеметрии list of dict Список словарей — очень частый учебный и практический формат
Практический вывод: не заучивайте коллекции как четыре формальных термина из учебника. Для каждой новой задачи спрашивайте себя: мне важен порядок? нужно ли менять состав? ищу ли я значение по имени? нужны ли только уникальные элементы?
Пример «по имени, а не по номеру» простыми словами. У автопилота тысячи настроек. Одна из них называется RTL_ALT — высота, связанная с возвратом домой (конкретные единицы и смысл зависят от прошивки; сейчас нам важно только имя). Если хранить настройки простым списком чисел, пришлось бы помнить: «высота RTL — это третий элемент списка». Завтра список перестроят, и третий элемент станет уже другой настройкой. Словарь решает это иначе: вы пишете обращение к словарю params и в квадратных скобках указываете имя RTL_ALT. Python сам находит нужное значение по этому имени. Номер позиции в памяти вам помнить не нужно.
↑ К оглавлению

4. Список list

Создание, индексы, срезы

Список создают квадратными скобками. Элементы разделяют запятыми. Порядок сохраняется.

modes = ["STABILIZE", "LOITER", "GUIDED", "AUTO", "RTL"]
print(modes)
print(len(modes))      # сколько элементов
print(modes[0])        # первый — STABILIZE
print(modes[-1])       # последний — RTL
print(modes[2:4])      # с индекса 2 до 4 не включая → GUIDED, AUTO
Индексация с нуля. Первый элемент — это modes[0], а не modes[1]. Если запросить индекс за пределами списка, Python возбудит исключение IndexError. Пока список короткий, перед обращением можно проверить длину через len(modes).
Отрицательный индекс считает с конца: -1 — последний элемент, -2 — предпоследний. Это удобно, когда длина списка меняется, а «последняя точка маршрута» всё равно нужна.

Добавление и изменение

modes = ["LOITER", "GUIDED"]
modes.append("AUTO")     # добавить в конец
modes[0] = "STABILIZE"   # заменить элемент
print(modes)

Полезные методы на старте:

ДействиеПримерСмысл
Добавить в конец modes.append("RTL") Самый частый способ пополнить список
Вставить по позиции modes.insert(1, "LOITER") Сдвиг остальных вправо
Удалить значение modes.remove("AUTO") Удаляет первое совпадение
Удалить по индексу last = modes.pop() pop() без аргумента удаляет последний элемент
Найти индекс modes.index("GUIDED") ValueError, если нет такого элемента
Посчитать вхождения modes.count("LOITER") Сколько раз значение встречается

Обход списка и enumerate

Обход значит: взять элементы по одному и с каждым что-то сделать. В Python для этого обычно пишут цикл for.

# Список учебных имён режимов полёта (порядок важен)
modes = ["GUIDED", "AUTO", "RTL"]

# Простой обход: на каждом шаге mode — очередная строка из списка
for mode in modes:
    print("mode:", mode)

# enumerate — встроенная функция Python.
# Она к каждому элементу «приклеивает» номер позиции (с нуля).
# i    — номер (0, 1, 2, ...)
# mode — сам элемент списка
for i, mode in enumerate(modes):
    print(f"[{i}] {mode}")
Зачем enumerate. В миссии вам почти всегда нужны и номер точки, и сама точка. enumerate отдаёт оба значения сразу. Отдельный счётчик «i = 0; i = i + 1» писать не нужно, пока нет особой причины.
Ожидаемый вывод второго цикла: строки [0] GUIDED, затем [1] AUTO, затем [2] RTL.
↑ К оглавлению

5. Кортеж tuple

Кортеж похож на список: есть порядок и индексы. Главное отличие — после создания кортеж не предназначен для изменения на месте. Нельзя сделать wp[0] = ... и нельзя append.

# (широта, долгота, высота_м)
wp = (55.751244, 37.618423, 40.0)
lat, lon, alt_m = wp    # распаковка
print(lat, lon, alt_m)
print(wp[2])            # высота

Список кортежей — удобная учебная миссия:

mission = [
    (55.751244, 37.618423, 0.0),
    (55.752000, 37.620000, 40.0),
    (55.753500, 37.615000, 40.0),
]

for i, wp in enumerate(mission):
    lat, lon, alt_m = wp
    print(f"WP{i}: {lat:.6f}, {lon:.6f}, {alt_m:.1f} m")
Зачем tuple, если есть list? Кортеж сигнализирует читателю кода: «это фиксированная структура из N полей». Случайно дописать пятую координату в середину миссии через append к «самой точке» уже не получится — точка не список. Позже, в dataclasses (K09), мы сделаем ещё более читаемые точки с именами полей. Сейчас tuple — хороший мост.
Скобки и запятая. Кортеж из одного элемента пишется с запятой: (42,) — иначе это просто число в скобках. На практике точки маршрута почти всегда из нескольких полей, так что путаница редка, но о ней стоит знать.
↑ К оглавлению

6. Словарь dict

Ключ и значение

Словарь хранит пары. Слева ключ, справа значение. Для параметров автопилота это самый естественный вид.

params = {
    "FRAME_CLASS": 1,
    "RTL_ALT": 5000,
    "FS_GCS_ENABLE": 1,
    "BATT_LOW_VOLT": 14.4,
}

print(params["RTL_ALT"])
params["BATT_LOW_VOLT"] = 14.2
print(params["BATT_LOW_VOLT"])
KeyError. Если обратиться к params["НЕТ_ТАКОГО"] квадратными скобками, а ключа нет, возникнет исключение KeyError. Для «параметр может отсутствовать» используйте get.

Безопасное чтение get

params = {"RTL_ALT": 5000}

print(params.get("RTL_ALT"))              # 5000
print(params.get("Q_ENABLE"))             # None, если ключа нет
print(params.get("Q_ENABLE", "not set"))  # значение по умолчанию
Зачем это в работе с БПЛА: разные прошивки и рамы отдают разный набор параметров. Код, который при отсутствии ключа всегда завершается ошибкой, ненадёжен. get с понятным значением по умолчанию — базовое правило аккуратного кода.

Обход словаря: метод items

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

Простыми словами, что такое items. У словаря есть метод (то есть встроенная команда объекта) с именем items. Когда вы пишете params.items(), Python не возвращает «просто список чисел». Он даёт последовательность пар. Каждая пара состоит из двух частей:

  • слева — ключ (имя параметра, строка вроде RTL_ALT);
  • справа — значение (число или другой объект, который хранится под этим именем).

Цикл for забирает эти пары по одной. Запись for name, value in ... называется распаковкой: первая часть пары попадает в переменную name, вторая — в переменную value. Имена переменных вы выбираете сами; name и value — просто понятные слова «имя» и «значение».

# Учебный словарь: имя параметра → значение
# (в PyCharm пишите обычные символы; на сайте для ZOO знаки сравнения могут быть полноширинными)
params = {
    "RTL_ALT": 5000,       # учебная высота RTL (смысл единиц — позже)
    "FS_GCS_ENABLE": 1,    # 1 часто значит «включено» в учебных примерах
}

# params.items() отдаёт пары (ключ, значение) по одной
for name, value in params.items():
    # name  — строка с именем параметра
    # value — то, что лежит в словаре под этим именем
    print(f"{name} = {value}")

# Только ключи (имена), без значений:
print("имена:", list(params.keys()))

# Только значения, без имён:
print("значения:", list(params.values()))
Что должно появиться на экране (в Python 3.7+ порядок ключей при обходе обычно совпадает с порядком вставки): две строки вида RTL_ALT = 5000 и FS_GCS_ENABLE = 1, затем список имён и список значений.
Зачем отдельный метод items, а не «просто for params»? Если написать for x in params:, Python по умолчанию перебирает только ключи (только имена). Значения сами не придут. Метод items как раз говорит: «дай и имя, и значение вместе». Есть ещё keys() — только имена, и values() — только значения. Для печати «параметр = число» почти всегда нужен именно items.

Проверка «есть ли такой ключ в словаре» — отдельная операция, не путайте её с items:

params = {"RTL_ALT": 5000}

# Оператор in для словаря проверяет КЛЮЧИ (имена), не значения
if "RTL_ALT" in params:
    print("Параметр RTL_ALT в словаре есть")
else:
    print("Параметра RTL_ALT нет")
↑ К оглавлению

7. Множество set

Множество хранит каждый элемент только один раз. Порядок при печати не стоит воспринимать как «закон маршрута» — для упорядоченных последовательностей берите list.

log_modes = ["LOITER", "LOITER", "GUIDED", "AUTO", "AUTO", "RTL"]
unique = set(log_modes)
print(unique)
print("AUTO" in unique)   # True
print("ACRO" in unique)   # False
unique.add("LAND")
unique.discard("ACRO")    # удалить, если есть; если нет - ошибки не будет
print(sorted(unique))     # sorted даёт обычный список по алфавиту
Когда set уместен: если нужно выяснить «какие режимы вообще встречались», «какие sysid были на канале», «какие ошибки prearm уже показывали оператору, чтобы не выводить их повторно». Если важен порядок точек миссии — set не подходит.
↑ К оглавлению

8. Что выбирать под задачу

Вопрос себеЕсли да
Важен порядок и состав будет меняться? list
Фиксированная структура из нескольких полей (точка, RGB, xyz)? tuple (позже dataclass)
Ищу значение по имени/строковому коду? dict
Нужны только уникальные элементы и проверка «уже видел»? set
Пачка однотипных записей с полями? list, а внутри dict или tuple
Частый рабочий шаблон: samples: list[dict] — список словарей телеметрии. Один элемент — один момент времени. Потом фильтруете, считаете max, строите график, пишете CSV (CSV будет в K07).
↑ К оглавлению

9. List comprehension (кратко)

Когда нужно построить новый список из старого, Python позволяет записать это компактно. Всегда умейте сделать то же самым циклом for.  Comprehension не обязателен, но часто очень удобен.

samples = [
    {"alt_m": 5.0, "mode": "GUIDED"},
    {"alt_m": 12.0, "mode": "AUTO"},
    {"alt_m": 20.0, "mode": "AUTO"},
]

alts = [s["alt_m"] for s in samples]
print(alts)

auto_only = [s for s in samples if s["mode"] == "AUTO"]
print(len(auto_only))

Тот же смысл развёрнуто:

alts = []
for s in samples:
    alts.append(s["alt_m"])
Не усложняйте. Если comprehension не читается с первого взгляда, пишите обычный цикл. Читаемость для инженера БПЛА важнее «красоты в одну строку».
↑ К оглавлению

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

ОшибкаЧто происходитКак думать
Обращение к modes[10], а элементов пять IndexError Проверьте len и индексы с нуля
params["NOPE"] без проверки KeyError get("NOPE", default) или проверка in
wp.append(...) где wp — tuple AttributeError Кортеж не список; меняйте mission-список, не саму точку-tuple
Путаете {} для set и dict {} — это пустой dict, не set Пустое множество: set()
Копирование списка через b = a и правка b Меняется и a Пока запомните: b = list(a) или a.copy(); глубокое копирование — позже
Ждёте от set стабильный «порядок миссии» Логика маршрута нарушается Маршрут — list
a = [1, 2, 3]
b = a          # b — ещё одно имя того же списка
b.append(4)
print(a)       # [1, 2, 3, 4] — сюрприз для новичка

a = [1, 2, 3]
b = list(a)    # поверхностная копия списка
b.append(4)
print(a)       # [1, 2, 3]
print(b)       # [1, 2, 3, 4]
↑ К оглавлению

11. Практика

Запускайте файлы из code/M01_python/ в venv проекта. Ниже — краткое содержание; полные скрипты уже в материалах курса.

Задача 4.1. Список режимов

Файл k04_task1_modes_list.py. Создайте список режимов, напечатайте первый и последний, срез, append, count, index.

Задача 4.2. Миссия из кортежей

Файл k04_task2_waypoint_tuples.py. Список из трёх точек (lat, lon, alt_m), обход с enumerate и распаковкой.

Задача 4.3. Словарь параметров

Файл k04_task3_params_dict.py. Учебный словарь PARAM, чтение, get для отсутствующего ключа, обновление, обход items. Не записывайте эти числа на реальный борт без понимания единиц и методики.

Задача 4.4. Уникальные режимы

Файл k04_task4_unique_modes_set.py. Из «лога» с повторами соберите set и ответьте, встречались ли AUTO и ACRO.

Задача 4.5. Пачка телеметрии

Файл k04_task5_telemetry_batch.py. Список словарей: высоты, минимум напряжения, только AUTO, предупреждение если высота выше учебного потолка 30 м. В файле сравнения — обычный Python; на сайте в общих правилах ZOO помните про полноширинные знаки в HTML-примерах.

Задача 4.6. Git commit

git add code/M01_python/k04_*.py
git commit -m "K04: list tuple dict set practice"
↑ К оглавлению

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

  1. Чем list отличается от tuple? Приведите пример точки маршрута.
  2. Почему параметры автопилота удобно хранить в dict, а не в list?
  3. Что вернёт params.get("NOPE", 0), если ключа нет?
  4. Зачем set при разборе лога режимов?
  5. Что такое индекс 0 и индекс -1 в списке?
  6. Чем опасна запись b = a для двух «списков», если потом менять b?
  7. Когда list comprehension уместен, а когда лучше обычный for?
  8. Какую коллекцию выберете для «очередь последних 50 высот» и почему?
↑ К оглавлению

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

  • Создаю list, читаю [0] и [-1], делаю срез, append
  • Обхожу список через for и enumerate
  • Создаю tuple точки (lat, lon, alt) и распаковываю поля
  • Понимаю, что tuple нельзя append
  • Создаю dict параметров, читаю по ключу, обновляю значение
  • Использую get со значением по умолчанию
  • Обхожу dict через items
  • Собираю set уникальных режимов и проверяю in
  • Могу выбрать list/tuple/dict/set под задачу БПЛА
  • Запускал k04_task1 … k04_task5 в venv
  • Сделал git commit по K04
Результат: чек-лист закрыт. Напишите ментору сообщение «K04 чек-лист закрыт». Далее K05 · Управление потоком и функции (углубление for/while, def, return, аргументы, type hints на стартовом уровне).
↑ К оглавлению

14. Что дальше

  • Предыдущий: K03 · Синтаксис и типы
  • Текущий: K04 · Коллекции
  • Следующий: K05 · Функции и управление потоком
  • Горизонт M1: K12 CLI-проект и гейт G1
Правка 1.1–1.2: учебный код с комментариями; раздел «Обход словаря: метод items» развёрнут; в 1.2 принята ручная правка ученика, исправлены битые автоссылки на имена файлов и структура секций HTML, уточнена формулировка про порядок ключей dict.
Напоминание про стиль: если в тексте урока или в ответе ментора снова встретится «проглоченная» фраза без расшифровки — пишите: Не понял фразу: «…». Стандарт: docs/05_EDITOR_STYLE_GUIDE.md.
↑ К оглавлению

 

 

Вторник, 11 августа 2026
k04. Коллекции list, tuple, dict, set