Коллекции - списки, кортежи, словари, множества
Структуры данных — о разделе → реализация и псевдокод операций → Коллекции в контексте ООП.
Ниже — list, dict, set в Python.
список.append(x) # динамический массив, в конец
элемент := список[i] # O(1) по индексу
словарь[ключ] := значение # хеш-таблица, в среднем O(1)
значение := словарь[ключ]
множество.add(x) # уникальные элементы
Play ITЗагрузка интерактивного демо…
Play ITЗагрузка интерактивного демо…
Коллекции
Последовательности
Типы данных не всегда единичны, и порой их нужно перечислять в форме коллекции, собирая несколько элементов.
Последовательности — это упорядоченные коллекции элементов, доступные по индексу. К ним относятся строки (str), списки (list), кортежи (tuple), а также объекты range, bytes и другие. Все последовательности поддерживают общие операции — индексирование, срезы, конкатенацию, повторение и проверку на вхождение.
Базовые операции:
- Индексирование:
seq[i]— получение элемента по индексу (от 0). - Срезы:
seq[start:stop:step]— извлечение подпоследовательности. - Длина:
len(seq)— количество элементов. - Принадлежность:
x in seq— возвращаетTrue, если x содержится в последовательности. - Конкатенация:
seq1 + seq2— объединение (если типы совместимы). - Повторение:
seq * n— повторение последовательности n раз.
s = "hello"
print(s[1]) # 'e'
print(s[1:4]) # 'ell'
print('h' in s) # True
print(s * 2) # 'hellohello'
lst = [1, 2, 3]
print(lst + [4, 5]) # [1, 2, 3, 4, 5]
Разбор:
s[1]берет символ по индексу.s[1:4]делает срез: старт включается, стоп не включается.'h' in sпроверяет вхождение подстроки.s * 2повторяет строку два раза.lst + [4, 5]создает новый список, исходныйlstостается без изменений.
Циклы for естественно работают с последовательностями:
for item in [10, 20, 30]:
print(item)
Особенность строк — неизменяемость.
При попытке модификации через индекс возникнет TypeError. Для списков и кортежей это ограничение действует только для кортежей (они тоже неизменяемы). Но, собственно, давайте по порядку.
Коллекции — это типы данных, предназначенные для хранения упорядоченных или неупорядоченных множеств элементов. В языке Python коллекции реализованы как встроенные классы (встроенные типы), обеспечивающие различные семантики доступа, модификации и организации данных.
Основные встроенные коллекции включают:
list— список;tuple— кортеж;set— множество;dict— словарь.
Каждая из этих структур отличается по четырём частым критериям выбора:
- Индексация — доступ к элементу по целочисленной позиции:
lst[0],t[1]. - Упорядоченность — при обходе элементы идут в предсказуемом порядке (как при вставке или по правилу сортировки).
- Изменяемость — можно ли после создания менять состав или значения.
- Дубликаты — допускаются ли повторяющиеся элементы (у
dictуникальны только ключи).
Сводка свойств встроенных коллекций
| Тип | Индексация по i | Упорядочен | Изменяем | Дубликаты | Литерал |
|---|---|---|---|---|---|
list | да | да | да | да | [1, 2, 3] |
tuple | да | да | нет | да | (1, 2, 3) |
set | нет | нет* | да | нет | {1, 2, 3} |
dict | по ключу** | да (с 3.7+)*** | да | ключи уникальны | {"a": 1} |
* у set порядок обхода в CPython может совпадать с порядком вставки, но на него не опираются в логике программы — тип считается неупорядоченным.
** у dict доступ d["имя"], а не d[0]; целочисленный индекс позиции не используется.
*** порядок вставки ключей гарантирован с Python 3.7.
Кратко по назначению:
list— упорядоченный изменяемый набор с позициями0, 1, 2…; дубликаты разрешены; удобен для очередей задач, логов, результатов запросов.tuple— как список по доступуt[i], но после создания не меняется; подходит для координат, записей "полей", ключейdict, возврата нескольких значений из функции.set— только уникальные элементы; позиционного индекса нет; быстрые проверкиx in sи операции|,&,-.dict— пары "ключ → значение"; ключи хешируемые и уникальные; значения могут повторяться.
lst = [10, 20, 10] # индекс, порядок, изменения, дубликаты
tpl = (10, 20, 10) # индекс и порядок; tpl[0] = 1 → TypeError
uniq = {10, 20, 10} # {10, 20}
phonebook = {"Аня": "+7…", "Борис": "+7…"} # phonebook["Аня"], не phonebook[0]
Выбор конкретной коллекции определяется требованиями к производительности, безопасности данных и логике алгоритма. Теория структур без привязки к Python — в разделе "Структуры данных"; обзор алгоритмов и сложности — в Алгоритмы и структуры данных.
Сводка операций по типам
list (изменяемый упорядоченный список):
| Действие | Синтаксис |
|---|---|
| Добавить в конец | append(value) |
| Вставить по индексу | insert(index, value) |
| Прочитать | lst[index] |
| Заменить | lst[index] = value |
| Удалить по индексу | pop(index) или del lst[index] |
| Удалить по значению | remove(value) |
set (уникальные элементы):
| Действие | Синтаксис |
|---|---|
| Добавить | add(value) |
| Удалить | remove(value) или discard(value) |
| Проверить | value in s |
dict (словарь):
| Действие | Синтаксис |
|---|---|
| Добавить или заменить | d[key] = value |
| Прочитать | d[key] или d.get(key) |
| Удалить | del d[key] или pop(key) |
tuple — только чтение t[index]; изменение после создания недоступно.
collections.deque (очередь с быстрыми операциями с обоих концов):
| Действие | Метод |
|---|---|
| В конец | append(value) |
| В начало | appendleft(value) |
| Снять с конца | pop() |
| Снять с начала | popleft() |
Списки (list).
Список — это упорядоченная, изменяемая коллекция элементов произвольных типов. Элементы списка хранятся последовательно и доступны по целочисленным индексам, начиная с нуля. Списки реализованы как динамические массивы с автоматическим управлением памятью.
Создание:
lst = [1, 2, 3]
fruits = ["яблоко", "банан", "вишня"]
print(fruits[1]) # банан — индекс с нуля
fruits[1] = "смородина"
Из другой последовательности (например, кортежа) — через конструктор list():
fruits = list(("яблоко", "банан", "вишня"))
Перебор в цикле for:
for item in fruits:
print(item)
Шаблон:
<список> = [<элемент>, <элемент>, ..., <элемент>]
<список>— имя переменной типаlist.- Элементы могут быть любого типа и не обязаны быть однородными.
Добавление элемента в список:
<список>.append(<значение>)
- Добавляет
<значение>в конец списка. - Метод изменяет исходный список.
Расширение списка итерируемым объектом:
<список>.extend(<итерируемый_объект>)
- Добавляет все элементы из
<итерируемый_объект>(например, другого списка) в конец текущего списка.
Вставка элемента по индексу:
<список>.insert(<индекс>, <значение>)
- Вставляет
<значение>в позицию<индекс>, сдвигая остальные элементы вправо.
Удаление элемента по значению:
<список>.remove(<значение>)
- Удаляет первое вхождение
<значение>из списка. - Если значение отсутствует — вызывается исключение
ValueError.
Удаление и получение элемента по индексу:
<элемент> = <список>.pop([<индекс>])
- Удаляет и возвращает элемент по указанному
<индекс>. По умолчанию — последний элемент (-1).
items = ["яблоко", "банан", "вишня"]
items.remove("банан") # по значению
last = items.pop() # снимает "вишня" с конца
del items[0] # удалить по индексу
items.clear() # оставить пустой список []
# del items # удалить имя items из программы
Разбор:
remove("банан")удаляет первое совпадение по значению.pop()удаляет и возвращает элемент, поэтому результат сохранен вlast.del items[0]удаляет элемент по индексу.clear()очищает содержимое списка, но сам объект списка остается существовать.
Сортировка списка:
<список>.sort(key=<функция>, reverse=<булево_значение>)
- Сортирует список на месте.
key— функция для получения ключа сравнения.reverse=True— сортировка по убыванию.
Разворот списка
Упорядоченный список можно пройти с конца к началу пятью распространёнными способами. Главное различие — меняется ли исходный объект или создаётся новая последовательность.
| Способ | Код | Исходный list | Что возвращается |
|---|---|---|---|
Метод reverse() | lst.reverse() | меняется | None |
| Срез | lst[::-1] | без изменений | новый list |
reversed() | list(reversed(lst)) | без изменений | новый list |
Цикл с insert(0, …) | см. ниже | без изменений | новый list |
Цикл с append по индексам | см. ниже | без изменений | новый list |
1. Метод reverse() — на месте
my_list = [1, 2, 3, 4, 5]
my_list.reverse()
print(my_list) # [5, 4, 3, 2, 1]
list.reverse() переворачивает элементы в том же объекте и возвращает None. Переменная my_list по-прежнему ссылается на изменённый список.
2. Срез [::-1] — новый список
my_list = [1, 2, 3, 4, 5]
reversed_list = my_list[::-1]
print(reversed_list) # [5, 4, 3, 2, 1]
print(my_list) # [1, 2, 3, 4, 5]
Синтаксис lst[start:stop:step] с step = -1 идёт от последнего индекса к нулевому. Исходный список остаётся прежним. Тот же приём для строк — в однострочных приёмах.
3. Встроенная reversed()
my_list = [1, 2, 3, 4, 5]
reversed_list = list(reversed(my_list))
reversed() возвращает итератор, а не список. Для материализации нужен list(...). Исходная коллекция не меняется. В цикле for x in reversed(lst) копию всего списка создавать не обязательно.
4. Цикл с insert(0, …)
my_list = [1, 2, 3, 4, 5]
r_list = []
for i in my_list:
r_list.insert(0, i)
print(r_list) # [5, 4, 3, 2, 1]
Каждый insert(0, i) сдвигает уже вставленные элементы, поэтому на длинных списках сложность растёт квадратично. Способ полезен на учебных задачах, в продакшене его редко выбирают.
5. Цикл с append по индексам с конца
my_list = [1, 2, 3, 4, 5]
r_list = []
for i in range(len(my_list) - 1, -1, -1):
r_list.append(my_list[i])
print(r_list) # [5, 4, 3, 2, 1]
Линейная работа по длине списка: индексы идут от len - 1 до 0 с шагом -1. Читается нагляднее, чем вариант с insert, но уступает срезу и reversed() по лаконичности.
Разворот на месте — lst.reverse(). Копия в обратном порядке при сохранении исходного порядка — lst[::-1] или list(reversed(lst)). Срез и reversed() — привычные идиомы; циклы помогают понять индексы и оценку сложности.
Индексация и срезы.
Доступ к элементам осуществляется по индексу:
lst[0] # первый элемент
lst[-1] # последний элемент
Срезы позволяют извлекать подпоследовательности:
lst[1:3] # элементы с индексами 1 и 2
lst[::2] # каждый второй элемент
lst[::-1] # весь список в обратном порядке (новый list)
Срезы возвращают новый объект list. Они поддерживают шаг, отрицательные индексы и обратный порядок. Подробнее про все способы разворота — выше.
Списки имеют следующие методы:
append(x)- Добавляет элемент x в конец списка.extend(iterable)- Добавляет все элементы итерируемого объекта в конец.- Вставляет элемент x на позицию i.
insert(i, x)
remove(x)- Удаляет первое вхождение элемента x.pop([i])- Удаляет и возвращает элемент по индексу (по умолчанию — последний).clear()- Удаляет все элементы, список остаётся[].count(x)- Возвращает количество вхождений элемента x.sort(key=None, reverse=False)- Сортирует список in-place.reverse()- Переворачивает список in-place.copy()- Создаёт поверхностную копию списка.
numbers = [5, 2, 8, 1]
numbers.sort()
print(numbers) # [1, 2, 5, 8]
numbers.reverse()
print(numbers) # [8, 5, 2, 1]
Разбор:
sort()сортирует список на месте и меняет порядок элементов в текущем объекте.reverse()также работает на месте и инвертирует порядок (см. разворот списка).- чтобы сохранить исходный порядок, используйте
sorted(numbers)и срезnumbers[::-1]илиlist(reversed(numbers)).
Методы sort и reverse модифицируют исходный объект и возвращают None.
Списки гетерогенны (могут быть разных типов), изменяемы (допускается добавление, удаление, замена элементов), и представляют собой массив указателей на объекты. При расширении происходит перераспределение памяти с запасом.
Бинарные списки: array.array используются для эффективного хранения числовых данных большого объёма. Этот тип представляет собой массив примитивных значений (например, целых или вещественных чисел) фиксированного размера.
import array
arr = array.array('i', [1, 2, 3]) # массив 32-битных целых
Такие списки занимают меньше памяти по сравнению с обычным списком, быстрее при последовательном доступе и поддерживают те же операции, что и списки (индексация, срезы), но только для однотипных данных. Но - не поддерживают гетерогенные данные.
Если же требуется получить и использовать именно индекс, а не элемент, тогда используется функция enumerate():
my_list = ["яблоко", "банан", "апельсин", "груша"]
for index, item in enumerate(my_list):
print(f"Индекс: {index}, Элемент: {item}")
Кортежи (tuple)
Кортеж — это неизменяемый список. Всё.
# Список (можно менять)
my_list = [1, 2, 3]
my_list[0] = 999 # работает
# Кортеж (нельзя менять)
my_tuple = (1, 2, 3)
my_tuple[0] = 999 # Ошибка!
Это даёт нам:
- защиту от случайных изменений;
- возможность использовать как ключ;
- возможность возвращать несколько значений из функции;
- производительность (он занимает меньше памяти).
# Кортеж — всё, что не меняется
coordinates = (55.752, 37.615) # координаты Красной площади
rgb_color = (255, 0, 0) # красный цвет
menu_position = ('File', 'Open') # путь в меню
# Список — то, что меняется
users = ['alex', 'john', 'anna'] # будут добавляться/удаляться
cart = [1, 2, 3] # товары в корзине (пополняется)
Если тебе нужно менять коллекцию — бери список (list). Если не нужно менять и просто читать — бери кортеж (tuple). А если не уверен — бери список, это проще. Кортежи — это оптимизация и защита от дурака (от себя же).
Кортеж — это упорядоченная, неизменяемая коллекция элементов произвольных типов. После создания кортеж нельзя модифицировать: нельзя добавлять, удалять или заменять элементы. Кортежи неизменяемые (чтобы гарантировать целостность данных), хэшируемые (сам кортеж может использовать в качестве ключа в словаре или элементе множества), производительны и применяются часто для представления составных данных, например, координат (x, y) или возврат нескольких значений из функции.
Пример:
t = (1, 2, 3)
t = 1, 2, 3 # скобки опциональны
coords = ("помидор", "огурец", "лук")
print(coords[1]) # огурец
Из последовательности — конструктор tuple() (часто с двойными скобками внутри, чтобы не спутать с группировкой выражений):
coords = tuple(("помидор", "огурец", "лук"))
После создания элемент нельзя заменить — будет TypeError:
coords = ("помидор", "огурец", "лук")
# coords[1] = "морковка" # TypeError —'tuple' object does not support item assignment
Удалить можно только весь кортеж через del, отдельные элементы не снимаются.
Кортежи поддерживают ограниченный набор методов:
count(x)— количество вхождений элемента x.index(x[, start[, end]])— индекс первого вхождения x.
Поскольку кортежи неизменяемы, методы модификации отсутствуют.
Создание кортежа:
<кортеж> = (<элемент>, <элемент>, ..., <элемент>)
или
<кортеж> = <элемент>, <элемент>, ..., <элемент>
Получение количества вхождений значения в кортеж:
<число> = <кортеж>.count(<значение>)
- Возвращает количество элементов, равных
<значение>.
Получение индекса первого вхождения значения:
<индекс> = <кортеж>.index(<значение>[, <начало>[, <конец>]])
- Возвращает индекс первого совпадения
<значение>в пределах[начало:конец].
Множества (set)
Множество — это неупорядоченная, изменяемая коллекция уникальных элементов. Элементы множества должны быть хэшируемыми (т.е. иметь метод __hash__ и быть сравнимыми).
Создание:
s = {1, 2, 3}
s = set([1, 2, 3]) # из итерируемого
# Список с дубликатами
items = ["тигр", "лев", "панда", "тигр", "заяц", "лев"]
# Превращаем в множество — дубликаты исчезнут!
unique_items = set(items)
print(unique_items) # {'тигр', 'лев', 'панда', 'заяц'}
Разбор:
set(items)создает множество из итерируемого объекта.- множество хранит только уникальные значения, повторы удаляются автоматически.
- порядок элементов у
setне предназначен для бизнес-логики.
Пустое множество создаётся только через set(), так как {} интерпретируется как пустой словарь.
Множества автоматически устраняют дубликаты:
{1, 1, 2} # → {1, 2}
Множества бывают изменяемыми и неизменяемыми.
Методы изменяемых множеств (set):
add(x)— добавить элемент.remove(x)— удалить элемент; ошибка, если нет.discard(x)— удалить, если есть; ошибки не возникает.pop()— удалить и вернуть произвольный элемент.clear()— очистить множество.
frozenset — неизменяемая версия множества. Поддерживает все операции чтения и теоретико-множественные операции, но не позволяет модифицировать содержимое. Полезен как ключ в словаре или элемент другого множества.
# set - изменяемый
mutable_set = {1, 2, 3}
mutable_set.add(4) # Работает
mutable_set.remove(2) # Работает
# frozenset - неизменяемый
frozen = frozenset({1, 2, 3})
# frozen.add(4) # Ошибка — AttributeError
# frozen.remove(2) # Ошибка — AttributeError
# frozenset хэшируемый
hash(frozenset({1, 2, 3})) # Работает
# set не хэшируемый
# hash({1, 2, 3}) # Ошибка — TypeError
# frozenset можно использовать как ключ
d = {frozenset({1, 2}): "value"} # Работает
# set нельзя использовать как ключ
# d = {{1, 2} — "value"} # Ошибка: TypeError
Объединение: | или .union()
first = {"тигр", "панда", "заяц"}
second = {"лев", "заяц", "медведь"}
all_animals = first | second
print(all_animals) # Все звери без повторов
Создание множества:
<множество> = {<элемент>, <элемент>, ..., <элемент>}
или
<множество> = set(<итерируемый_объект>)
- Все элементы должны быть хэшируемыми.
- Автоматически удаляются дубликаты.
Добавление элемента в множество:
<множество>.add(<значение>)
- Добавляет
<значение>в множество, если оно ещё не присутствует.
Удаление элемента из множества (с ошибкой при отсутствии):
<множество>.remove(<значение>)
- Удаляет
<значение>. Если его нет — возникает исключениеKeyError.
Удаление элемента из множества (без ошибки):
<множество>.discard(<значение>)
- Удаляет
<значение>, если оно есть. Не вызывает исключение при отсутствии.
Объединение двух множеств:
<новое_множество> = <множество_1> | <множество_2>
или
<новое_множество> = <множество_1>.union(<множество_2>)
- Возвращает новое множество, содержащее все уникальные элементы из обоих исходных.
Преобразование:
# Преобразование set в frozenset
frozen = frozenset({1, 2, 3})
# Преобразование frozenset в set
mutable = set(frozenset({1, 2, 3}))
Play ITЗагрузка интерактивного демо…
Словари (dict)
Словарь — это изменяемая коллекция пар "ключ-значение", где ключи уникальны и хэшируемы, а значения могут быть любыми объектами. Доступ к значениям осуществляется по ключу. Словари реализованы как хеш-таблицы с открытой адресацией.
Создание:
d = {'a': 1, 'b': 2}
d = dict(a=1, b=2)
Ключи должны быть хэшируемыми (числа, строки, кортежи из хэшируемых объектов и т.д.). Значения же произвольные объекты, включая изменяемые. Словари сохраняют порядок вставки с Python 3.7+ (в 3.6 — как особенность CPython, с 3.7 — как гарантия языка).
Методы словаря
Девять методов, которые чаще всего нужны в повседневном коде (плюс copy() для копии):
| Метод | Назначение |
|---|---|
get(key[, default]) | Значение по ключу; если ключа нет — default (по умолчанию None), без KeyError |
keys() | Представление ключей (итерируемое) |
values() | Представление значений |
items() | Пары (ключ, значение) |
update(other) | Добавить или перезаписать пары из другого словаря или итерируемого |
setdefault(key, default) | Вернуть значение; если ключа нет — записать default и вернуть его |
pop(key[, default]) | Удалить ключ и вернуть значение; при отсутствии ключа — default или KeyError |
popitem() | Удалить и вернуть последнюю вставленную пару (ключ, значение) |
clear() | Удалить все пары |
copy() | Поверхностная копия |
keys(), values() и items() возвращают представления (views), а не списки. Для вывода в консоль удобно обернуть в list(...).
Один словарь — по шагам, как на шпаргалке:
Код ITЗагрузка примера кода…
Запись d[key] при отсутствии ключа вызывает KeyError (см. исключения). Метод get() подходит, когда отсутствие ключа — нормальный сценарий — конфиг, кэш, счётчики (freq.get(token, 0) + 1 ниже в практических примерах).
Пример — классический словарь-переводчик:
translator = {
"cat": "кошка",
"dog": "собака",
"house": "дом",
"sun": "солнце",
"book": "книга"
}
word = input("Введите слово на английском: ")
translation = translator.get(word, "Не знаю такого слова")
print(f"Перевод: {translation}")
Разбор:
- словарь
translatorхранит парыанглийское_слово -> перевод. input(...)получает ключ для поиска в словаре.get(word, "Не знаю такого слова")возвращает перевод или значение по умолчанию, если ключ отсутствует.- финальная
f-строка печатает уже готовый результат поиска.
Создание словаря:
<словарь> = {<ключ>: <значение>, <ключ>: <значение>, ..., <ключ>: <значение>}
или
<словарь> = dict(<ключ>=<значение>, ...)
- Ключи должны быть хэшируемыми; значения — произвольными.
Получение значения по ключу (с безопасным доступом):
<значение> = <словарь>.get(<ключ>[, <значение_по_умолчанию>])
- Возвращает значение, связанное с
<ключ>, или<значение_по_умолчанию>, если ключ отсутствует.
Установка значения по умолчанию при отсутствии ключа:
<значение> = <словарь>.setdefault(<ключ>, <значение_по_умолчанию>)
- Если
<ключ>отсутствует, добавляет пару<ключ>: <значение_по_умолчанию>и возвращает это значение. - Если ключ есть — возвращает существующее значение.
Обновление словаря парами из другого словаря:
<словарь>.update(<другой_словарь>)
- Добавляет или заменяет пары "ключ-значение" из
<другой_словарь>в текущий словарь.
Удаление по ключу и снятие последней пары:
<значение> = <словарь>.pop(<ключ>[, <значение_по_умолчанию>])
<пара> = <словарь>.popitem()
<словарь>.clear()
popвозвращает значение удалённого ключа;popitem— кортеж(ключ, значение)последней вставки;clearочищает весь словарь.
Итерация по ключам, значениям и парам:
for <ключ> in <словарь>.keys():
...
for <значение> in <словарь>.values():
...
for <ключ>, <значение> in <словарь>.items():
...
.keys(),.values(),.items()возвращают представления (views), а не списки.
Практические сценарии
Подсчёт частот
text = ["api", "db", "api", "cache", "api", "db"]
freq = {}
for token in text:
freq[token] = freq.get(token, 0) + 1
print(freq) # {'api': 3, 'db': 2, 'cache': 1}
Разбор:
- цикл
for token in textпроходит по каждому элементу списка. freq.get(token, 0)берет текущее значение счетчика или0, если ключ встречается впервые.- выражение
+ 1увеличивает счетчик, после чего обновленное значение записывается вfreq[token].
Удаление дублей с сохранением порядка
items = ["a", "b", "a", "c", "b"]
unique = list(dict.fromkeys(items))
print(unique) # ['a', 'b', 'c']
Очередь задач
from collections import deque
queue = deque(["task-1", "task-2"])
queue.append("task-3")
current = queue.popleft()
print(current) # task-1
Типичные ошибки при работе с коллекциями
- Использование изменяемого объекта как ключа словаря.
- Ожидание стабильного порядка у
setв бизнес-логике. - Неявное копирование вложенных структур вместо
copy.deepcopy. - Выбор
listдля операции "проверить наличие" в больших наборах, где нуженset.
Для корректной копии вложенных структур:
import copy
config = {"limits": [1, 2, 3]}
clone = copy.deepcopy(config)
Связанные материалы
- Базовые алгоритмы и Big O: Алгоритмы и структуры данных
- Логика обхода в циклах: Управляющие конструкции
- Ленивые обходы и пайплайны: Итераторы и генераторы
- Сложность операций с коллекциями: Нотация Большое O · Lab / Big-O — 1128
- Задачи ЕГЭ/олимпиад (
list,dict,set,Counter): Lab / 1122
Как выбирать коллекцию без лишних сомнений
Полезная инженерная эвристика:
- Нужен порядок и изменения — берите
list. - Нужен порядок без изменений — берите
tuple. - Нужен быстрый доступ по ключу — берите
dict. - Нужна уникальность и операции пересечения/объединения — берите
set.
Если структура выбрана верно, код становится быстрее и проще, чем при попытке "решить всё списком".
В задачах ЕГЭ и олимпиад те же типы постоянно встречаются в коде — list для массива чисел, set для "уже видели", dict/Counter для частот — готовые примеры с разбором — Lab / 1122. Почему x in list в цикле даёт O(n²), а x in set — O(n) — Lab / 1128, ловушки Python.