← Назад к вопросам
Как устроены неизменяемые объекты в памяти?
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, короткие строки)
- Можно использовать как ключи словарей и элементы множеств
- Хеш вычисляется один раз и не меняется
- Копирование оптимизировано (часто просто возвращается исходный объект)
- Счётчик ссылок отслеживает количество ссылок на объект
- Кортежи неизменяемы, но могут содержать изменяемые объекты