Комментарии (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"} # Работает
Преимущества неизменяемых структур
- Потокобезопасность — несколько потоков могут безопасно использовать один объект
- Кэширование — хеш не меняется, можно кэшировать результаты
- Использование как ключей — работают в словарях и sets
- Предсказуемость — нет побочных эффектов от изменения
- Функциональное программирование — естественнее с неизменяемостью
- Отладка — проще отследить ошибки без изменений состояния
Рекомендации
- Используй dataclass(frozen=True) для новых классов
- Используй namedtuple для простых структур данных
- Используй Pydantic для валидированных конфигов
- Избегай изменения объектов, используемых как ключи
- Для ML-моделей оборачивай гиперпараметры в frozen dataclass
- Предпочитай неизменяемость по умолчанию, меняй только когда необходимо
Неизменяемость — это не просто граммотная практика, а способ написать надежный и масштабируемый код.