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

Как устроены неизменяемые объекты в памяти?

3.0 Senior🔥 141 комментариев
#Python Core

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

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

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

Устройство неизменяемых объектов в памяти Python

Неизменяемые объекты (immutable objects) — это основной строительный блок Python. Их архитектура в памяти отличается от изменяемых объектов и имеет важные последствия для производительности и поведения программы.

1. Что такое неизменяемые объекты?

Неизменяемые объекты в Python — это объекты, которые не могут быть изменены после создания:

# Неизменяемые типы
int_val = 42          # int
float_val = 3.14      # float
str_val = "hello"     # str
tuple_val = (1, 2, 3) # tuple
frozen = frozenset([1, 2, 3])  # frozenset
bool_val = True       # bool
none_val = None       # NoneType

# Попытка изменить строку
s = "hello"
s[0] = "H"  # TypeError: str object does not support item assignment

2. Кеширование малых целых чисел

Python кэширует целые числа от -5 до 256 для оптимизации памяти:

a = 5
b = 5
print(a is b)  # True — один объект в памяти

c = 257
d = 257
print(c is d)  # False — разные объекты!

# Но после присваивания:
e = 257
f = 257
print(e is f)  # False

# А в результате выражения:
print((256 + 1) is (256 + 1))  # True — оптимизация компилятора

Почему так происходит? Python держит список часто используемых целых чисел в памяти для ускорения их использования.

3. Интернирование строк

Python интернирует (кэширует) строки для оптимизации:

# Строки создаются один раз
a = "hello"
b = "hello"
print(a is b)  # True — один объект

# Но если строка содержит спецсимволы
c = "hello world!"  # содержит пробел
d = "hello world!"
print(c is d)  # False — разные объекты

# Интернирование вручную
import sys
e = sys.intern("hello world")
f = sys.intern("hello world")
print(e is f)  # True

4. Структура объекта в памяти

Каждый объект в Python имеет заголовок:

import sys

# Размер объекта
print(sys.getsizeof(5))        # 28 байт (int)
print(sys.getsizeof("hello"))   # 54 байт (str)
print(sys.getsizeof((1, 2, 3))) # 56 байт (tuple)

# Структура в памяти:
# [refcount | type | hash | value_data]
# где:
# - refcount: число ссылок на объект
# - type: указатель на тип (class)
# - hash: кэшированный хеш
# - value_data: данные объекта

5. Хеширование неизменяемых объектов

Неизменяемые объекты можно использовать как ключи в словарях:

# ✅ Можно
dict_key = {
    (1, 2, 3): "tuple key",
    "string": "str key",
    42: "int key",
    frozenset([1, 2]): "frozenset key"
}

# ❌ Нельзя — список изменяемый
try:
    bad_dict = {[1, 2]: "list key"}
except TypeError:
    print("TypeError: unhashable type: list")

# Хеш вычисляется один раз и кэшируется
obj = "hello"
print(hash(obj))  # одно и то же значение
print(hash(obj))  # хеш кэширован в объекте

6. Копирование неизменяемых объектов

import copy

# Для неизменяемых объектов копирование оптимизировано
original = (1, 2, 3)
shallow = copy.copy(original)
deep = copy.deepcopy(original)

print(original is shallow)  # True — копирование не нужно
print(original is deep)     # True — копирование не нужно

# Для строк
s1 = "immutable"
s2 = s1  # просто ссылка
print(s1 is s2)  # True

7. Ссылки и счётчик ссылок

import sys

a = 42
print(sys.getrefcount(a))  # много ссылок (кэширование + параметр функции)

b = [1, 2, 3]
print(sys.getrefcount(b))  # меньше

c = b  # новая ссылка
print(sys.getrefcount(b))  # увеличилось

del c  # удалили ссылку
print(sys.getrefcount(b))  # уменьшилось

8. Костыли и подводные камни

# Кортежи содержат ссылки на объекты
t = ([1, 2, 3], "string")
print(t[0] is not t)  # True
t[0].append(4)  # можно изменить список внутри кортежа!
print(t)  # ([1, 2, 3, 4], string)

# Строки в памяти
s = "a" * 1000
print(sys.getsizeof(s))  # ~1000 байт

# Но малые строки кэшируются
small = "a"
print(sys.getsizeof(small))  # 50 байт + строка

9. Оптимизация производительности

# ✅ Используй неизменяемые объекты как ключи
config = {
    ("db", "host"): "localhost",
    ("db", "port"): 5432,
}

# ✅ Кэшируй дорогие операции со строками
import functools

@functools.lru_cache(maxsize=None)
def expensive_function(immutable_arg: str) -> str:
    return immutable_arg.upper()

# ❌ Не используй изменяемые объекты в множествах
set_with_list = {[1, 2]}  # TypeError

Итоги

Ключевые особенности неизменяемых объектов:

  • Python кэширует часто используемые значения (-5..256 для int, короткие строки)
  • Можно использовать как ключи словарей и элементы множеств
  • Хеш вычисляется один раз и не меняется
  • Копирование оптимизировано (часто просто возвращается исходный объект)
  • Счётчик ссылок отслеживает количество ссылок на объект
  • Кортежи неизменяемы, но могут содержать изменяемые объекты
Как устроены неизменяемые объекты в памяти? | PrepBro