list vs tuple в Python — в чём разница и когда что использовать

Коротко

list — изменяемая (mutable) последовательность. tuple — неизменяемая (immutable) последовательность. Это ключевое отличие, из которого вытекают все остальные: tuple быстрее, занимает меньше памяти и может быть ключом словаря.

Что такое list

list — упорядоченная изменяемая коллекция. Элементы можно добавлять, удалять, менять по индексу. Создаётся через квадратные скобки [] или конструктор list().

# Создание и изменение
fruits = ['яблоко', 'банан', 'вишня']
fruits.append('груша')         # ['яблоко', 'банан', 'вишня', 'груша']
fruits[1] = 'апельсин'         # ['яблоко', 'апельсин', 'вишня', 'груша']
fruits.remove('вишня')         # ['яблоко', 'апельсин', 'груша']

# Сортировка in-place
numbers = [3, 1, 4, 1, 5]
numbers.sort()                 # [1, 1, 3, 4, 5]

# List comprehension
squares = [x ** 2 for x in range(5)]  # [0, 1, 4, 9, 16]

list хранит массив указателей на объекты и резервирует дополнительную память для будущих append — это называется overallocation.

Что такое tuple

tuple — упорядоченная неизменяемая коллекция. После создания нельзя добавить, удалить или заменить элементы. Создаётся через круглые скобки () или конструктор tuple().

# Создание
point = (10, 20)
rgb = (255, 128, 0)
singleton = (42,)              # запятая обязательна для tuple из одного элемента

# Распаковка
x, y = point                   # x=10, y=20

# Используется как ключ словаря
grid = {}
grid[(0, 0)] = 'start'
grid[(1, 2)] = 'finish'

# Named tuple для читаемости
from collections import namedtuple
User = namedtuple('User', ['name', 'age', 'city'])
u = User('Анна', 25, 'Москва')
print(u.name)                  # 'Анна'

tuple из одного элемента требует запятую: (42,). Без запятой (42) — это просто число в скобках. Классическая ловушка на собеседовании.

Ключевые отличия

list tuple
Мутабельность Изменяемый Неизменяемый
Синтаксис [1, 2, 3] (1, 2, 3)
Методы изменения append, extend, insert, remove, pop, sort Нет
Хешируемость Нет (нельзя в set/dict key) Да (если все элементы хешируемы)
Память Больше (overallocation) Меньше (фиксированный размер)
Скорость создания Медленнее Быстрее
Скорость итерации Чуть медленнее Чуть быстрее
Типичное использование Коллекция однородных элементов Запись с фиксированной структурой

Когда использовать list

  • Коллекция элементов, которая будет расти или уменьшаться
  • Результат фильтрации, маппинга, сортировки
  • Данные одного типа: список пользователей, список цен
  • Стек или очередь (хотя для очереди лучше deque)
  • Когда нужно менять элементы по индексу
# Сбор результатов
results = []
for row in data:
    if row['status'] == 'active':
        results.append(row['id'])

# Сортировка
users = [('Анна', 25), ('Борис', 30), ('Вика', 22)]
users.sort(key=lambda x: x[1])  # по возрасту

Когда использовать tuple

  • Фиксированная структура: координаты (x, y), RGB (r, g, b)
  • Ключ словаря или элемент множества
  • Возврат нескольких значений из функции
  • Данные, которые не должны случайно измениться
  • Аргументы *args в функциях (Python передаёт их как tuple)
# Ключ словаря
cache = {}
cache[(user_id, date)] = result

# Возврат нескольких значений
def min_max(values):
    return min(values), max(values)

lo, hi = min_max([3, 1, 4, 1, 5])  # lo=1, hi=5

# Константные данные
WEEKDAYS = ('Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс')
HTTP_OK = (200, 201, 204)

Типичная ошибка

Думать, что tuple полностью иммутабельный, если внутри есть изменяемые объекты:

# tuple с list внутри — list можно изменить!
t = ([1, 2], [3, 4])
t[0].append(99)          # Работает! t = ([1, 2, 99], [3, 4])
t[0] = [5, 6]            # TypeError: tuple не позволяет заменить элемент

# Такой tuple нельзя хешировать
hash(t)                   # TypeError: unhashable type: 'list'

Неизменяемость tuple означает, что нельзя заменить ссылки на объекты, но сами объекты (если они mutable) можно менять. Это важно понимать для собеседования.

Вопросы с собеседований

В чём ключевая разница между list и tuple? — list мутабельный, tuple иммутабельный. Из этого следует: tuple хешируемый (может быть ключом dict), занимает меньше памяти и создаётся быстрее.

Почему tuple быстрее list? — Tuple имеет фиксированный размер, не нужен overallocation. Python кеширует маленькие tuple (до 20 элементов) для переиспользования. Создание tuple — одна операция, а list требует аллокации с запасом.

Можно ли изменить элемент tuple? — Нельзя заменить элемент по индексу. Но если элемент сам по себе мутабельный (например, list), его содержимое можно изменить. Это не нарушает иммутабельность tuple — ссылка остаётся прежней.

Как создать tuple из одного элемента?(42,) — с запятой. Без запятой (42) — просто число. Альтернатива: tuple([42]).

Когда list лучше tuple? — Когда коллекция меняется: добавление, удаление, сортировка. Или когда семантически это набор однородных элементов переменной длины (список заказов, список ID).

FAQ

Насколько tuple экономнее по памяти?

Примерно на 10–20% для небольших коллекций. Для tuple из 5 int — около 80 байт, для list — около 120 байт. Разница в overallocation: list резервирует место для будущих append.

Можно ли конвертировать list в tuple и обратно?

Да: tuple(my_list) и list(my_tuple). Это создаёт новый объект, оригинал не меняется. Конвертация за O(n).

Зачем tuple, если есть namedtuple и dataclass?

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

Что такое tuple unpacking и зачем он нужен?

Это распаковка: a, b, c = (1, 2, 3). Работает и с list, но чаще используется с tuple. Позволяет вернуть несколько значений из функции и сразу присвоить их переменным. Поддерживает *rest для перехвата оставшихся элементов: first, *rest = (1, 2, 3, 4).