Комментарии (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)
Ключевые моменты
- Фиксированный размер — кортеж занимает ровно столько памяти, сколько нужно
- Указатели — хранит указатели на объекты, а не сами объекты
- Неизменяемость — позволяет использовать как ключи и оптимизировать кэширование
- Кэширование — малые кортежи кэшируются для экономии памяти
- Хеширование — возможно, если все элементы хешируемы
Кортежи более эффективны по памяти, чем списки, благодаря отсутствию резерва и оптимизациям интерпретатора.