UpPetto

This is a real course generated by UpPetto — unedited.

AI-generated. Two models wrote it, a third checked every risky claim against sources, a fourth re-checked.

Create my own course

list/tuple/dict/set вместо массивов и struct: что выбрать и почему

core выбрать подходящую встроенную коллекцию Python для конкретной структуры данных, которую вы раньше реализовали бы вручную в C

В C структура данных — это решение о раскладке байтов в памяти: struct фиксирует набор полей, массив фиксирует размер и тип элементов, хеш-таблицу вы пишете сами (массив бакетов + список коллизий + функция хеширования). В Python выбор коллекции — это не решение о памяти, а решение о семантике: изменяемо это или нет, важен ли порядок, нужен ли доступ по ключу, допустимы ли дубликаты. Память и хеширование интерпретатор берёт на себя.

Четыре встроенные коллекции и их роль:

  • list — динамическая, изменяемая, гетерогенная последовательность; растёт и уменьшается сама, без realloc.
  • tuple — неизменяемая последовательность: «запись», единое целое; хешируем, поэтому годится в ключи dict и элементы set.
  • dict — встроенная хеш-таблица «ключ → значение», в среднем O(1) на доступ.
  • set — хранение уникальных элементов и быстрая проверка принадлежности, в среднем O(1).

Соответствие с тем, что вы писали руками в C:

Что вы писали в C Замена в Python Почему
int arr[N] фиксированного размера list растёт и уменьшается сам, без realloc
struct point { int x, y; } как неизменяемая запись tuple фиксированный набор полей, неизменяем, хешируем
ручная hash table (массив бакетов + linked list коллизий) dict в среднем O(1), без своей функции хеширования
bitset или сортированный массив + bsearch для проверки принадлежности set проверка in в среднем O(1), без сортировки и бинарного поиска

Типичные ловушки C-программиста:

  1. Видеть list и tuple как «два синтаксиса для массива» и всегда брать list «на всякий случай изменяемый». tuple говорит читателю и интерпретатору: этот набор значений — единое целое и не изменится; бонусом он хешируем (хеш ключа должен быть стабилен, а изменяемый объект такой стабильности не даёт).
  2. «Свой dict» через два параллельных списка (keys[], values[]) и линейный поиск индекса — прямой перенос C-паттерна «массив структур + перебор», убивающий главное преимущество словаря.
  3. list там, где нужна уникальность и быстрая проверка принадлежности: x in my_list — линейный перебор, x in my_set — хеш-поиск.

Разобранный пример

Задача: посчитать частоту слов в тексте — в C вы решали бы её через массив struct { char* word; int count; } и линейный (или самописный hash) поиск при каждом слове.

Наивный «переведённый C»:

words = text.split()
entries = []  # список [слово, счётчик]
for w in words:
    found = False
    for e in entries:
        if e[0] == w:
            e[1] += 1
            found = True
            break
    if not found:
        entries.append([w, 1])

Это O(n·m) — при каждом слове линейный перебор накопленных записей. Идиоматично:

from collections import Counter
counts = Counter(text.split())

Даже без Counter обычный dict через counts[w] = counts.get(w, 0) + 1 уже правильное направление — суть в том, что ключевой доступ заменяет линейный перебор.

Попробуй сейчас

У вас есть код на «переведённом C»:

names = ["Ann", "Bob", "Cid"]
ages = [30, 25, 41]

def age_of(name):
    for i in range(len(names)):
        if names[i] == name:
            return ages[i]
    return None

Перепишите это без параллельных списков и без линейного поиска, используя одну коллекцию Python. Ожидаемый результат: age_of("Bob") работает за счёт прямого доступа по ключу, без цикла.

Получилось, если…

Вы справились, если решение — это dict вида {"Ann": 30, "Bob": 25, "Cid": 41} с доступом через ages.get(name), и вы можете объяснить, почему это быстрее исходного варианта при большом количестве имён (в среднем O(1) против O(n) на каждый запрос).

Вывод

list — для изменяемой последовательности, tuple — для неизменяемой записи (и как ключ dict), dict — вместо любой ручной hash table, set — вместо ручной проверки уникальности/принадлежности. Выбор коллекции в Python кодирует смысл данных, а не их раскладку в памяти.

AI-generated · source-grounded review

🛡 Fact-checked: 3 risky claims verified · 2 removed · confidence: high · figures: 1 · a second model checked these claims and agreed
[verified] dict обеспечивает поиск по ключу в среднем за O(1), set — проверку принадлежности в среднем за O(1)
База знаний: dict — встроенная хеш-таблица для быстрого поиска, set — эффективная проверка принадлежности
[verified] tuple неизменяем и может служить ключом словаря, list — нет
База знаний: tuple — неизменяемая последовательность, используемая в качестве ключей словаря
[removed] list — динамический массив, аналог std::vector из C++
Сравнение с C++-контейнером отсутствует в базе знаний; оставлено только описание list как динамической гетерогенной последовательности
[verified] Фигура (figure-svg): соответствие int ids[10] → list, struct Point → tuple/dict, ручная хеш-таблица → dict
Полностью соответствует разделу базы 'Коллекции вместо массивов и структур'
[removed] Сравнение list с std::vector из C++ убрано — этого сопоставления нет в базе знаний
[removed] Формулировки O(1) для dict/set оставлены только в виде 'в среднем O(1)', как в базе знаний
Key concepts: list tuple dict set
Tell me more 🔒 Didn't understand — explain simply 🔒 Show examples 🔒 Sources 🔒

On your own course these buttons answer instantly, quizzes track what you've mastered, and lessons adapt to your gaps. Write my course

Check yourself

1. Вы портируете из C структуру для хранения RGB-цвета: `struct Color { unsigned char r, g, b; };`. Что идиоматичнее всего для одной такой записи, если цвет после создания не меняется?
2. Что лучше выбрать вместо ручной хеш-таблицы (массив + линейный поиск) для поиска значения по ключу?
3. Нужно посчитать количество уникальных IP-адресов в большом логе. Какая коллекция подходит лучше всего?
On your own course, these are marked as you answer