← Назад к вопросам

Как кортеж хранятся в памяти?

2.0 Middle🔥 161 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Хранение кортежей в памяти Python

Кортежи — это неизменяемые последовательности, и их хранение в памяти имеет особенности.

1. Структура PyTupleObject в памяти

В CPython кортеж представлен структурой:

# Внутренняя структура (упрощённо)
typedef struct {
    PyObject_HEAD          # Заголовок объекта (счётчик ссылок, тип)
    Py_ssize_t ob_size;   # Размер кортежа (количество элементов)
    PyObject *ob_item[1]; # Массив указателей на объекты
} PyTupleObject;

2. Распределение памяти

import sys

# Пустой кортеж занимает 56 байт
print(sys.getsizeof(()))  # 56

# Каждый элемент добавляет 8 байт (на 64-битной системе)
t1 = (1,)
print(sys.getsizeof(t1))  # 64 (56 + 8)

t2 = (1, 2, 3)
print(sys.getsizeof(t2))  # 80 (56 + 24)

# Но это НЕ включает размер самих объектов
print(sys.getsizeof(t2) + sys.getsizeof(1) + sys.getsizeof(2) + sys.getsizeof(3))

3. Указатели на объекты

Кортеж хранит только указатели на объекты, а не сами объекты:

a = [1, 2, 3]
t = (a, "hello", 42)

# t содержит указатель на список a, не копию
print(id(a))        # Адрес списка
print(id(t[0]))     # Тот же адрес

# Изменение списка видно через кортеж
a.append(4)
print(t[0])  # [1, 2, 3, 4]

4. Кэширование малых кортежей

Python оптимизирует память, кэшируя малые кортежи (до 256 элементов):

# Пустые кортежи всегда кэшируются
t1 = ()
t2 = ()
print(id(t1) == id(t2))  # True, один объект в памяти

# Кортежи из малых объектов кэшируются
t3 = (1, 2, 3)
t4 = (1, 2, 3)
print(id(t3) == id(t4))  # True на одной строке!

# Но если создавать отдельно...
t5 = (1, 2, 3)
t6 = (1, 2, 3)
print(id(t5) == id(t6))  # Может быть False в зависимости от оптимизации

5. Словари хешируемых объектов

Кортежи хешируемы, если все элементы хешируемы:

# Хешируемый кортеж
t1 = (1, 2, "hello")
print(hash(t1))  # Вычисляется один раз

# Можно использовать как ключ
data = {
    (1, 2): "value1",
    (3, 4): "value2"
}

# Нехешируемый кортеж
t2 = (1, [2, 3])  # Содержит список
try:
    hash(t2)
except TypeError as e:
    print(e)  # unhashable type: 'list'

6. Сравнение списков и кортежей

import sys

list_obj = [1, 2, 3]
tuple_obj = (1, 2, 3)

print(sys.getsizeof(list_obj))    # 72 (больше из-за резерва)
print(sys.getsizeof(tuple_obj))   # 80 (точный размер)

# Список имеет резерв для расширения
list_obj.append(4)  # Быстро, использует резерв
tuple_obj = tuple_obj + (4,)     # Создаёт новый кортеж

7. Поведение при копировании

t1 = (1, 2, [3, 4])
t2 = t1  # Поверхностное копирование (просто ещё один указатель)

print(id(t1) == id(t2))  # True, один объект
print(id(t1[2]) == id(t2[2]))  # True, список тот же

# Изменение списка видно в обе стороны
t1[2].append(5)
print(t2)  # (1, 2, [3, 4, 5])

8. Оптимизация в интерпретаторе

# Интерпретатор может кэшировать часто используемые кортежи
# Например, при вызове функций с несколькими аргументами

def func(*args):
    print(type(args))  # Это всегда кортеж
    print(sys.getsizeof(args))

func(1, 2, 3)

Ключевые моменты

  • Фиксированный размер — кортеж занимает ровно столько памяти, сколько нужно
  • Указатели — хранит указатели на объекты, а не сами объекты
  • Неизменяемость — позволяет использовать как ключи и оптимизировать кэширование
  • Кэширование — малые кортежи кэшируются для экономии памяти
  • Хеширование — возможно, если все элементы хешируемы

Кортежи более эффективны по памяти, чем списки, благодаря отсутствию резерва и оптимизациям интерпретатора.