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

Какие знаешь иммутабельные объекты?

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

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

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

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

Иммутабельные (неизменяемые) объекты в Python

Иммутабельность — свойство объекта, при котором его состояние не может быть изменено после создания. Это критично для написания безопасного и предсказуемого кода.

Встроенные иммутабельные типы

1. int (целое число)

# int иммутабелен
x = 5
print(id(x))  # 140734667394528

# Попытка "изменить" создаёт новый объект
x = x + 1
print(id(x))  # 140734667394560 (другой объект!)

# Python оптимизирует малые числа (-5 до 256)
x = 5
y = 5
print(id(x) == id(y))  # True (один объект)

z = 257
w = 257
print(id(z) == id(w))  # False (разные объекты)

2. str (строка)

# Строка иммутабельна
text = "hello"
print(id(text))  # 140734667394528

# Попытка "изменить" создаёт новую строку
text = text + " world"
print(id(text))  # 140734667394560 (новая строка)

# Строковые методы возвращают новую строку
original = "Hello World"
lowercased = original.lower()  # Не меняет original
print(original)     # "Hello World" (без изменений)
print(lowercased)   # "hello world" (новая строка)

3. tuple (кортеж)

# Кортеж иммутабелен
t = (1, 2, 3)
print(id(t))  # 140734667394528

# Нельзя изменить элемент
t[0] = 99  # TypeError: 'tuple' object does not support item assignment

# Нельзя добавить элемент
t.append(4)  # AttributeError: 'tuple' object has no attribute 'append'

# Но элементы могут быть мутабельными!
t = ([1, 2], 3)  # Кортеж содержит список
t[0].append(4)  # OK! Список иммутабелен, но его содержимое можно менять
print(t)  # ([1, 2, 4], 3)

4. frozenset (неизменяемое множество)

# frozenset иммутабелен
fs = frozenset([1, 2, 3])
print(id(fs))  # 140734667394528

# Нельзя добавить или удалить
fs.add(4)  # AttributeError: 'frozenset' object has no attribute 'add'

# Можно использовать как ключ в словаре (в отличие от set)
my_dict = {fs: "value"}  # OK

# Но обычный set нельзя
my_set = {1, 2, 3}
my_dict = {my_set: "value"}  # TypeError: unhashable type: 'set'

5. bytes (байты)

# bytes иммутабелен
b = b"hello"
print(id(b))  # 140734667394528

# Нельзя изменить
b[0] = 99  # TypeError: 'bytes' object does not support item assignment

# Но bytearray мутабелен
ba = bytearray(b"hello")
ba[0] = ord('H')
print(ba)  # bytearray(b'Hello')

6. bool, None, float

# Все иммутабельны
true_val = True
false_val = False
none_val = None
float_val = 3.14

# Изменение создаёт новый объект
true_val = False  # Новое назначение, не изменение

Мутабельные типы (для сравнения)

1. list (список)

# Список мутабелен
my_list = [1, 2, 3]
print(id(my_list))  # 140734667394528

# Изменение НЕ создаёт новый объект
my_list[0] = 99
print(id(my_list))  # 140734667394528 (тот же объект!)
print(my_list)  # [99, 2, 3]

# Добавление элемента
my_list.append(4)
print(id(my_list))  # 140734667394528 (всё ещё тот же объект)
print(my_list)  # [99, 2, 3, 4]

2. dict (словарь)

# Словарь мутабелен
my_dict = {"a": 1, "b": 2}
print(id(my_dict))  # 140734667394528

# Изменение
my_dict["a"] = 99
print(id(my_dict))  # 140734667394528 (тот же объект)
print(my_dict)  # {"a": 99, "b": 2}

# Добавление
my_dict["c"] = 3
print(id(my_dict))  # 140734667394528
print(my_dict)  # {"a": 99, "b": 2, "c": 3}

3. set (множество)

# Множество мутабельно
my_set = {1, 2, 3}
print(id(my_set))  # 140734667394528

my_set.add(4)
print(id(my_set))  # 140734667394528 (тот же объект)
print(my_set)  # {1, 2, 3, 4}

Создание собственных иммутабельных объектов

1. Использование @dataclass с frozen=True

from dataclasses import dataclass

@dataclass(frozen=True)
class Point:
    x: int
    y: int

point = Point(1, 2)
print(id(point))  # 140734667394528

# Попытка изменить
point.x = 5  # FrozenInstanceError: cannot assign to field 'x'

# Можно использовать как ключ
my_dict = {point: "location"}
my_set = {point, Point(3, 4)}

2. Использование slots и свойств

class ImmutablePoint:
    __slots__ = ('_x', '_y')
    
    def __init__(self, x, y):
        object.__setattr__(self, '_x', x)
        object.__setattr__(self, '_y', y)
    
    @property
    def x(self):
        return self._x
    
    @property
    def y(self):
        return self._y
    
    def __setattr__(self, name, value):
        raise AttributeError("can't set attribute")
    
    def __hash__(self):
        return hash((self._x, self._y))
    
    def __eq__(self, other):
        return isinstance(other, ImmutablePoint) and \
               self._x == other._x and self._y == other._y

point = ImmutablePoint(1, 2)
print(point.x)  # 1
point.x = 5  # AttributeError: can't set attribute

# Можно использовать как ключ
my_dict = {point: "location"}

3. Использование namedtuple

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])
point = Point(1, 2)

print(point.x)  # 1
point.x = 5  # AttributeError: can't set attribute

# Хеширует автоматически
my_dict = {point: "location"}
my_set = {point, Point(3, 4)}

Преимущества иммутабельности

1. Безопасность в многопоточной программе

import threading

# Иммутабельный объект безопасен без блокировок
immutable_tuple = (1, 2, 3)

def thread1():
    for i in range(1000000):
        _ = immutable_tuple  # Безопасно

def thread2():
    for i in range(1000000):
        _ = immutable_tuple  # Безопасно

t1 = threading.Thread(target=thread1)
t2 = threading.Thread(target=thread2)
t1.start()
t2.start()
t1.join()
t2.join()
# Никакой race condition!

# Мутабельный список требует синхронизации
from threading import Lock
mutable_list = [1, 2, 3]
lock = Lock()

def safe_append(item):
    with lock:
        mutable_list.append(item)  # Требует блокировки

2. Использование как ключ в словаре

# Иммутабельные объекты можно использовать как ключи
coordinates = {
    (0, 0): "origin",
    (1, 1): "point_a",
    (2, 2): "point_b"
}

# Мутабельные — нельзя
my_dict = {
    [0, 0]: "origin"  # TypeError: unhashable type: 'list'
}

3. Понятность и предсказуемость кода

def process_data(data: tuple):
    # Знаем, что data не изменится внутри функции
    for item in data:
        print(item)
    # data всё ещё то же самое

# vs

def process_data(data: list):
    # data может быть изменён
    for item in data:
        print(item)
    # data может быть другим!
    # (если функция его изменила)

4. Кеширование результатов

from functools import lru_cache

# Кеш работает только с иммутабельными аргументами
@lru_cache(maxsize=128)
def expensive_function(data: tuple):
    return sum(data)

result = expensive_function((1, 2, 3))  # OK, кешируется

# С мутабельными — не работает
@lru_cache(maxsize=128)
def process_list(data: list):
    return sum(data)

result = process_list([1, 2, 3])  # TypeError: unhashable type: 'list'

Таблица иммутабельности

ТипИммутабеленХешируетЮзкейс
intДаДаЧисла, счётчики
strДаДаТекст, идентификаторы
tupleДаДаНеизменяемые наборы данных
frozensetДаДаУникальные значения, ключи
bytesДаДаДвоичные данные
boolДаДаЛогические значения
NoneДаДаОтсутствие значения
listНетНетДинамические коллекции
dictНетНетКлюч-значение хранилище
setНетНетУникальные элементы
bytearrayНетНетИзменяемые байты

Выводы

  • Иммутабельные объекты (int, str, tuple, frozenset, bytes) не меняются после создания
  • Мутабельные объекты (list, dict, set) можно изменять
  • Преимущества иммутабельности: потокобезопасность, использование как ключи, кеширование
  • Используй tuple вместо list, когда данные не меняются
  • Используй frozenset вместо set, когда нужны ключи словаря
  • Создавай собственные иммутабельные классы с @dataclass(frozen=True)

Иммутабельность — основа чистого и безопасного кода!

Какие знаешь иммутабельные объекты? | PrepBro