Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как понять, что объект изменяемый
Это базовый, но ОЧЕНЬ важный навык. Неправильное понимание — источник багов. За 10+ лет я видел столько проблем из-за этого.
1. Базовое разделение
Изменяемые (Mutable):
list, dict, set, bytearray, custom objects
Неизменяемые (Immutable):
int, float, str, tuple, frozenset, bytes, bool, None
Но это не полный ответ! Нужно ПОНИМАТЬ, почему так работает.
2. Тест: попытка изменить объект
# Изменяемый: можно изменить содержимое
my_list = [1, 2, 3]
my_list[0] = 999 # ✅ РАБОТАЕТ
print(my_list) # [999, 2, 3]
# Неизменяемый: нельзя изменить содержимое
my_tuple = (1, 2, 3)
my_tuple[0] = 999 # ❌ TypeError: tuple does not support item assignment
# Неизменяемый: нельзя переприсвоить элемент
my_string = "hello"
my_string[0] = 'H' # ❌ TypeError: str does not support item assignment
3. Методы, которые работают только с мutable объектами
# Методы для list (изменяемый)
my_list = [1, 2, 3]
my_list.append(4) # ✅ Работает
my_list.pop() # ✅ Работает
my_list.sort() # ✅ Работает
my_list[0] = 999 # ✅ Работает
# Методы для tuple (неизменяемый)
my_tuple = (1, 2, 3)
my_tuple.append(4) # ❌ AttributeError: tuple has no attribute 'append'
my_tuple[0] = 999 # ❌ TypeError
# Методы для str (неизменяемый)
my_str = "hello"
my_str[0] = 'H' # ❌ TypeError
# Но можно создать новый объект:
my_str = my_str.replace('h', 'H') # ✅ Возвращает новую строку
4. Ключевой признак: id() меняется?
import copy
# Изменяемый объект: id остаётся, содержимое меняется
my_list = [1, 2, 3]
original_id = id(my_list)
my_list.append(4) # Изменяем
print(f"ID до: {original_id}")
print(f"ID после: {id(my_list)}")
print(f"Одно и то же? {original_id == id(my_list)}") # True
# Неизменяемый объект: нельзя изменить, только создать новый
my_tuple = (1, 2, 3)
original_id = id(my_tuple)
my_tuple = my_tuple + (4,) # Создаёт НОВЫЙ tuple
print(f"ID до: {original_id}")
print(f"ID после: {id(my_tuple)}")
print(f"Одно и то же? {original_id == id(my_tuple)}") # False
Правило: Если id() остаётся после "изменения" → объект изменяемый.
5. Проверка: создание новых объектов
# Строка (неизменяемая): каждая операция создаёт новую строку
name = "John"
name = name.upper() # Новый объект!
print(id(name)) # Другой ID
# Список (изменяемый): методы часто меняют на месте
data = [1, 2, 3]
data.reverse() # Меняет на месте, не возвращает новый список
print(data) # [3, 2, 1]
# Но некоторые методы создают новый объект:
new_data = sorted(data) # Возвращает новый список
print(data) # [3, 2, 1] — не изменился
print(new_data) # [1, 2, 3] — новый
6. Практические признаки мutable vs immutable
# ❌ Неправильно: передача как ключ dict (нужно immutable)
my_dict = {
[1, 2]: "value" # ❌ TypeError: unhashable type: 'list'
}
# ✅ Правильно: tuple как ключ
my_dict = {
(1, 2): "value" # ✅ Работает, tuple immutable
}
# ❌ Неправильно: mutable default argument
def bad_func(items=[]): # Опасно! Список переиспользуется
items.append(1)
return items
# ✅ Правильно: None (immutable) как умолчание
def good_func(items=None): # None immutable
if items is None:
items = []
items.append(1)
return items
7. Вложенные структуры: неполная иммутабельность
# Tuple сам по себе immutable, но содержимое может быть mutable!
data = ([1, 2], [3, 4]) # tuple содержит списки
data[0][0] = 999 # ✅ РАБОТАЕТ! Меняем содержимое вложенного списка
print(data) # ([999, 2], [3, 4])
data[0] = [999] # ❌ TypeError: tuple does not support item assignment
# Вывод: tuple immutable в отношении СВОЕЙ структуры,
# но не в отношении СОДЕРЖИМОГО вложенных mutable объектов
8. Использование hash()
Изменяемые объекты обычно не имеют стабильного hash:
# Неизменяемые объекты: имеют hash
print(hash(42)) # ✅
print(hash("hello")) # ✅
print(hash((1, 2, 3))) # ✅
# Изменяемые объекты: НЕ имеют hash
print(hash([1, 2, 3])) # ❌ TypeError: unhashable type: 'list'
print(hash({"a": 1})) # ❌ TypeError: unhashable type: 'dict'
# Поэтому изменяемые объекты нельзя использовать как ключи dict или элементы set
my_set = {(1, 2), (3, 4)} # ✅ Tuple — OK
my_set = {[1, 2], [3, 4]} # ❌ List — TypeError
9. Встроенная функция isinstance()
# Проверка типа (работает, но не идеально)
def is_mutable(obj):
"""Приблизительная проверка"""
mutable_types = (list, dict, set, bytearray)
return isinstance(obj, mutable_types)
print(is_mutable([1, 2])) # True
print(is_mutable((1, 2))) # False
print(is_mutable("hello")) # False
print(is_mutable({"a": 1})) # True
# Но это не 100% правда для custom объектов
class MyClass:
pass
print(is_mutable(MyClass())) # False (но объект мутабелен!)
10. Custom объекты — всегда изменяемы (по умолчанию)
class User:
def __init__(self, name):
self.name = name
user = User("John")
user.name = "Jane" # ✅ Можно изменить
print(f"ID до: {id(user)}")
user.name = "Bob"
print(f"ID после: {id(user)}")
print(f"Одно и то же? {id(user) == id(user)}") # True
# Custom объекты изменяемы по умолчанию
# Если нужен immutable объект — используй @dataclass с frozen=True
from dataclasses import dataclass
@dataclass(frozen=True)
class ImmutableUser:
name: str
user2 = ImmutableUser("John")
user2.name = "Jane" # ❌ FrozenInstanceError
11. Проверка: есть ли метод hash?
# Immutable объекты обычно hashable
print(hasattr(str, '__hash__')) # True
print(hasattr(tuple, '__hash__')) # True
print(hasattr(int, '__hash__')) # True
# Mutable объекты обычно нет
print(hasattr(list, '__hash__')) # False
print(hasattr(dict, '__hash__')) # False
print(hasattr(set, '__hash__')) # False
# Как проверить конкретный объект
print(hash.__doc__) # Что такое hash
print(hash([1, 2])) # TypeError
12. Практический чеклист
# Как проверить, изменяемый ли объект?
test_obj = [1, 2, 3]
# Способ 1: попытка изменить и поймать ошибку
try:
test_obj[0] = 999
print("Изменяемый")
except TypeError:
print("Неизменяемый")
# Способ 2: проверить наличие методов изменения
print(hasattr(test_obj, 'append')) # list → True
print(hasattr(test_obj, 'pop')) # list → True
print(hasattr(test_obj, 'sort')) # list → True
test_obj2 = (1, 2, 3)
print(hasattr(test_obj2, 'append')) # tuple → False
# Способ 3: проверить hash
try:
hash(test_obj)
print("Неизменяемый (hashable)")
except TypeError:
print("Изменяемый (unhashable)")
Таблица: Полный справочник
| Тип | Mutable | Методы | Можно ключ dict | Hash |
|---|---|---|---|---|
| list | ✅ | append, pop, sort | ❌ | ❌ |
| dict | ✅ | update, pop, clear | ❌ | ❌ |
| set | ✅ | add, remove, pop | ❌ | ❌ |
| bytearray | ✅ | append, pop | ❌ | ❌ |
| tuple | ❌ | только viewing | ✅ | ✅ |
| str | ❌ | replace, split (новый) | ✅ | ✅ |
| int | ❌ | - | ✅ | ✅ |
| frozenset | ❌ | - | ✅ | ✅ |
| bytes | ❌ | - | ✅ | ✅ |
Золотое правило
Изменяемые объекты можно менять на месте, неизменяемые нет. Проверяй: "Могу ли я вызвать метод, который меняет это?" Если да — изменяемый. Используй эти знания для правильного проектирования: неизменяемые как ключи dict, изменяемые только для локального мутирования.