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

Какие знаешь иммутабельные структуры данных?

1.3 Junior🔥 121 комментариев
#Python Core

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

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

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

Иммутабельные структуры данных в Python

Иммутабельные (неизменяемые) типы данных — это структуры, которые после создания нельзя изменить. Это критически важно для безопасности потоков, кеширования и использования в качестве ключей. Разберу все встроенные и популярные варианты.

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

str (строка) — иммутабельна

s = "hello"
# s[0] = "H"  # TypeError: 'str' object does not support item assignment

# Если кажется, что меняешь — на самом деле создаёшь новую
s2 = s.upper()  # Создана новая строка
print(s)        # "hello" (исходная не изменилась)
print(s2)       # "HELLO"

# Объединение тоже создаёт новую строку
s3 = s + "!"

int, float, bool — иммутабельны

x = 42
y = x
y = 43  # Создан новый объект, x не изменился
print(x)  # 42

# int кешируются (-5 до 256)
a = 256
b = 256
print(a is b)  # True (один и тот же объект)

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

None — иммутабельна

a = None
b = None
print(a is b)  # True

2. tuple (кортеж) — иммутабельна

Список, но неизменяемый.

t = (1, 2, 3)
# t[0] = 10  # TypeError: 'tuple' object does not support item assignment
# t.append(4)  # AttributeError: 'tuple' object has no attribute 'append'

# Но элементы могут быть мутабельными!
t = ([1, 2], [3, 4])
t[0].append(999)  # МОЖЕШЬ изменить список внутри!
print(t)  # ([1, 2, 999], [3, 4])

# Используешь для распаковки
a, b, c = (10, 20, 30)
print(a, b, c)  # 10 20 30

# Возврат нескольких значений
def get_user():
    return ("John", 30, "john@example.com")

name, age, email = get_user()

Производительность:

# Кортежи быстрее списков
import timeit

list_time = timeit.timeit(lambda: [1, 2, 3, 4, 5])
tuple_time = timeit.timeit(lambda: (1, 2, 3, 4, 5))
print(f"List: {list_time}, Tuple: {tuple_time}")
# Tuple быстрее примерно на 30%

3. frozenset — иммутабельное множество

Множество, но неизменяемое. Можешь использовать в качестве ключа.

# Обычное множество (мутабельное)
s = {1, 2, 3}
s.add(4)  # Можешь изменять

# Frozenset (иммутабельное)
fs = frozenset([1, 2, 3])
# fs.add(4)  # AttributeError: 'frozenset' object has no attribute 'add'

# Можешь использовать как ключ
data = {fs: "value"}  # OK
data[s] = "value"     # TypeError: unhashable type: 'set'

# Операции множества работают
fs1 = frozenset([1, 2, 3])
fs2 = frozenset([2, 3, 4])
print(fs1 & fs2)  # frozenset({2, 3}) пересечение
print(fs1 | fs2)  # frozenset({1, 2, 3, 4}) объединение

4. Иммутабельные типы из collections

namedtuple — именованный кортеж

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)

print(p.x)      # 10 (доступ как атрибут)
print(p[0])     # 10 (доступ как индекс)

# Всё ещё иммутабельна
# p.x = 15  # AttributeError: can't set attribute

# Неплохо для возврата нескольких значений
UserInfo = namedtuple('UserInfo', ['name', 'age', 'email'])
user = UserInfo("Alice", 30, "alice@example.com")

ChainMap — вид на несколько словарей (но не иммутабельна)

from collections import ChainMap

dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}

chain = ChainMap(dict1, dict2)
print(chain['a'])  # 1 (из первого)
print(chain['b'])  # 2 (из первого, приоритет)
print(chain['c'])  # 4 (из второго)

5. Типы из types

MappingProxyType — read-only вид на словарь

from types import MappingProxyType

original = {'a': 1, 'b': 2}
read_only = MappingProxyType(original)

print(read_only['a'])  # 1
# read_only['a'] = 10  # TypeError: 'mappingproxy' object does not support item assignment

# Но если меняешь original, меняется и view
original['a'] = 999
print(read_only['a'])  # 999

6. Иммутабельные структуры из сторонних библиотек

pyrsistent — функциональные иммутабельные структуры

from pyrsistent import v, m, s

# Иммутабельный список (PVector)
v1 = v(1, 2, 3)
v2 = v1.append(4)  # Возвращает новый вектор
print(v1)  # v(1, 2, 3) (исходный не изменился)
print(v2)  # v(1, 2, 3, 4)

# Иммутабельный словарь (PMap)
m1 = m(a=1, b=2)
m2 = m1.set('c', 3)  # Возвращает новый словарь
print(m1)  # pmap({'a': 1, 'b': 2})
print(m2)  # pmap({'a': 1, 'b': 2, 'c': 3})

# Иммутабельное множество (PSet)
s1 = s(1, 2, 3)
s2 = s1.add(4)  # Возвращает новое множество
print(s1)  # pset([1, 2, 3])
print(s2)  # pset([1, 2, 3, 4])

attrs с frozen=True

from attrs import define

@define(frozen=True)
class User:
    name: str
    age: int
    email: str

user = User("Alice", 30, "alice@example.com")
# user.age = 31  # attrs.exceptions.FrozenInstanceError

# Но можешь создать новый
user2 = attrs.evolve(user, age=31)
print(user.age)   # 30 (исходный не изменился)
print(user2.age)  # 31

dataclasses с frozen=True (Python 3.7+)

from dataclasses import dataclass

@dataclass(frozen=True)
class Product:
    name: str
    price: float
    stock: int

product = Product("Laptop", 999.99, 10)
# product.price = 899.99  # dataclasses.FrozenInstanceError

7. Кортежи с вложенными мутабельными объектами

Опасность: кортеж иммутабельна, но содержимое может меняться

data = ([1, 2, 3], {"key": "value"})

# Кортеж сам по себе иммутабельный
# data[0] = [4, 5, 6]  # TypeError

# Но содержимое можешь менять
data[0].append(4)  # OK
data[1]["new_key"] = "new_value"  # OK

print(data)  # ([1, 2, 3, 4], {'key': 'value', 'new_key': 'new_value'})

# Если нужна полная иммутабельность — используй вложенные иммутабельные типы
data = ((1, 2, 3), frozenset(['key', 'value']))  # Полностью иммутабельна

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

from dataclasses import dataclass

@dataclass(frozen=True)
class Color:
    r: int
    g: int
    b: int
    
    def __post_init__(self):
        # Валидация
        if not all(0 <= c <= 255 for c in (self.r, self.g, self.b)):
            raise ValueError("RGB values must be 0-255")
    
    @property
    def hex(self):
        return f"#{self.r:02x}{self.g:02x}{self.b:02x}"

# Использование
red = Color(255, 0, 0)
print(red.hex)  # #ff0000

# Как ключ в словаре
colors = {red: "Red", Color(0, 255, 0): "Green"}

Сравнение иммутабельных типов

Тип              | Иммутабельна | Hashable | Используем в | Производительность
-----------------|--------------|----------|------------------|----------------
str              | Да           | Да       | Ключи, sets | Быстрая
int, float, bool | Да           | Да       | Ключи, sets | Очень быстрая
tuple            | Да*          | Да       | Ключи, sets | Быстрая
frozenset        | Да           | Да       | Ключи, sets | Средняя
namedtuple       | Да           | Да       | Данные, ключи | Быстрая
list             | Нет          | Нет      | Коллекции | Быстрая
dict             | Нет          | Нет      | Коллекции | Быстрая
set              | Нет          | Нет      | Коллекции | Быстрая

* Если содержит мутабельные элементы — не полностью иммутабельна

Когда использовать иммутабельные типы

  1. Безопасность потоков (threading)
# Безопасно в многопоточности
shared_data = (1, 2, 3)  # Не нужны locks
  1. Ключи словарей
cache = {("user", 123): "John"}  # Можешь
data = {["user", 123]: "John"}   # Нельзя
  1. Хеширование и кеширование
hashed_config = hash(("debug", True, 30))  # OK
  1. Функциональное программирование
from functools import reduce

def process(data):
    # Функция не меняет входные данные
    return (data[0] * 2, data[1] + 10)  # Возвращает новый кортеж
  1. Явное намерение: это не должно меняться
from dataclasses import dataclass

@dataclass(frozen=True)
class Config:
    # Сигнал: конфиг не должен меняться
    debug: bool
    timeout: int
Какие знаешь иммутабельные структуры данных? | PrepBro