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

Приведи примеры неизменяемых структур в Python

1.0 Junior🔥 111 комментариев
#Python

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

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

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

Примеры неизменяемых структур в Python

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

Встроенные неизменяемые типы

1. Числа (int, float, complex)

x = 42
y = 42
print(id(x) == id(y))  # Может быть True (кэширование)

# Попытка изменить
x = 100  # Не изменяет, создает новый объект

Числа полностью неизменяемы. Операции не изменяют исходное число, а создают новое.

2. Строки (str)

s = "Hello"
print(s[0])  # 'H'

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

# Изменение создает новую строку
s_new = "J" + s[1:]  # "Jello"
print(s)  # "Hello" (исходная не изменилась)

Строки полностью неизменяемы. Все операции со строками создают новые строки.

3. Кортежи (tuple)

t = (1, 2, 3)
print(t[0])  # 1

# Попытка изменить
t[0] = 10  # TypeError: tuple object does not support item assignment

# Создание нового кортежа
t_new = (10,) + t[1:]  # (10, 2, 3)

# Используются как ключи в словарях
d = {(1, 2): "value"}  # Работает!
d = {[1, 2]: "value"}  # TypeError: unhashable type: 'list'

4. Frozenset

fs = frozenset([1, 2, 3])
print(2 in fs)  # True

# Попытка изменить
fs.add(4)  # AttributeError: 'frozenset' object has no attribute 'add'

# Может использоваться как ключ
d = {frozenset([1, 2]): "value"}  # Работает!
s = set([frozenset([1, 2]), frozenset([3, 4])])  # Работает!

Профессиональные неизменяемые структуры

1. Named Tuples

from collections import namedtuple

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

print(p.x, p.y)  # 10 20
print(p[0], p[1])  # 10 20

# Попытка изменить
p.x = 30  # AttributeError: can't set attribute

# Использование как ключа
d = {Point(1, 2): "origin"}  # Работает

# Создание нового с изменением (неизменяемая замена)
p_new = p._replace(x=30)  # Point(x=30, y=20)

2. Dataclasses с frozen=True (Python 3.7+)

from dataclasses import dataclass

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

user = User("Alice", 30)
print(user.name)  # Alice

# Попытка изменить
user.name = "Bob"  # FrozenInstanceError

# Может использоваться как ключ
d = {User("Alice", 30): "developer"}  # Работает!

# Создание нового с изменением
from dataclasses import replace
user_new = replace(user, name="Bob")  # User(name="Bob", age=30)

3. Pydantic BaseModel с frozen=True

from pydantic import BaseModel

class User(BaseModel):
    name: str
    age: int
    
    class Config:
        frozen = True

user = User(name="Alice", age=30)
print(user.name)  # Alice

# Попытка изменить
user.name = "Bob"  # ValidationError/FrozenError

# Валидация встроена
user = User(name="Alice", age="thirty")  # Автоматическая конверсия!

# Может использоваться как ключ
d = {user: "developer"}  # Работает

4. Python 3.10+ Dataclass defaults

from dataclasses import dataclass

@dataclass(frozen=True)
class Config:
    api_key: str
    timeout: int = 30
    retries: int = 3

config = Config(api_key="secret123")
print(config.timeout)  # 30

# Неизменяема и может быть ключом
configs = {config: "production"}

Для Data Science: Immutable структуры

1. NumPy с флагом writeable=False

import numpy as np

arr = np.array([1, 2, 3])
arr.flags.writeable = False

# Попытка изменить
arr[0] = 10  # ValueError: assignment destination is read-only

2. Pandas с copy=False и view

import pandas as pd

df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})

# View (обычно безопаснее для больших данных)
view = df.iloc[0:2]

# Но pandas не полностью неизменяем, нужна осторожность

3. Custom Immutable класс для ML-моделей

from dataclasses import dataclass
from typing import Tuple
import numpy as np

@dataclass(frozen=True)
class ModelConfig:
    learning_rate: float
    batch_size: int
    hidden_layers: Tuple[int, ...]
    activation: str
    
    def __post_init__(self):
        # Валидация
        if self.learning_rate <= 0:
            raise ValueError("learning_rate должен быть положительным")
        if self.batch_size <= 0:
            raise ValueError("batch_size должен быть положительным")

config = ModelConfig(
    learning_rate=0.001,
    batch_size=32,
    hidden_layers=(128, 64, 32),
    activation="relu"
)

# Может использоваться как ключ для кэширования результатов
model_cache = {}
model_cache[config] = trained_model

Вложенные неизменяемые структуры

from dataclasses import dataclass
from typing import Tuple

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

@dataclass(frozen=True)
class Polygon:
    points: Tuple[Point, ...]  # Кортеж неизменяемых точек
    name: str

p1 = Point(0, 0)
p2 = Point(1, 0)
p3 = Point(1, 1)

poly = Polygon((p1, p2, p3), "triangle")
print(poly.points[0])  # Point(x=0, y=0)

# Полностью неизменяема вглубь
poly_dict = {poly: "shape"}  # Работает

Преимущества неизменяемых структур

  1. Потокобезопасность — несколько потоков могут безопасно использовать один объект
  2. Кэширование — хеш не меняется, можно кэшировать результаты
  3. Использование как ключей — работают в словарях и sets
  4. Предсказуемость — нет побочных эффектов от изменения
  5. Функциональное программирование — естественнее с неизменяемостью
  6. Отладка — проще отследить ошибки без изменений состояния

Рекомендации

  1. Используй dataclass(frozen=True) для новых классов
  2. Используй namedtuple для простых структур данных
  3. Используй Pydantic для валидированных конфигов
  4. Избегай изменения объектов, используемых как ключи
  5. Для ML-моделей оборачивай гиперпараметры в frozen dataclass
  6. Предпочитай неизменяемость по умолчанию, меняй только когда необходимо

Неизменяемость — это не просто граммотная практика, а способ написать надежный и масштабируемый код.

Приведи примеры неизменяемых структур в Python | PrepBro