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

Что такое mutable и immutable типы данных в Python?

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

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

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

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

Что такое mutable и immutable типы данных в Python?

Mutable (изменяемые) типы — это объекты, содержимое которых можно изменять после создания без создания нового объекта. Immutable (неизменяемые) типы — это объекты, которые нельзя изменять; любое изменение создаёт новый объект.

Это фундаментальное различие влияет на производительность, безопасность кода и поведение памяти в Python.

Immutable (неизменяемые) типы

Встроенные immutable типы:

  • int, float, bool, complex — числовые типы
  • str — строки
  • tuple — кортежи
  • frozenset — неизменяемые множества
  • bytes — байты
  • None — особый объект
# String (неизменяемая)
s = "Hello"
print(id(s))           # адрес в памяти
s = s + " World"
print(id(s))           # НОВЫЙ адрес — создан новый объект!

# Демонстрация
s1 = "Hello"
s2 = s1
s1 = s1 + " World"
print(s1)  # "Hello World"
print(s2)  # "Hello" — s2 не изменилась!

# Tuple (неизменяемая)
t = (1, 2, 3)
t[0] = 10  # TypeError: tuple object does not support item assignment

# Но можно переприсвоить
t = (10,) + t[1:]  # Создаётся НОВЫЙ tuple

Почему это важно:

  1. Безопасность параметров функций
def modify_value(x):
    # Если x — immutable (int, str), функция не может
    # изменить оригинальное значение
    x = x + 10  # Создаётся новый int, x не меняется снаружи
    return x

original = 5
result = modify_value(original)
print(original)  # 5 — не изменилась

# Это отличается от mutable объектов!
  1. Использование как ключей словаря
# Immutable может быть ключом
d = {}
d[(1, 2)] = "tuple key"     # ОК
d["string"] = "string key"  # ОК
d[(1, 2, (3, 4))] = "nested" # ОК

# Mutable не может быть ключом
d[[1, 2]] = "list key"      # TypeError: unhashable type: list
d[{"a": 1}] = "dict key"    # TypeError: unhashable type: dict
  1. Интерирование — может использоваться в set
# Immutable можно добавить в set
s = set()
s.add((1, 2))       # ОК
s.add("hello")      # ОК

# Mutable нельзя добавить в set
s.add([1, 2])       # TypeError: unhashable type: list
s.add({"a": 1})     # TypeError: unhashable type: dict

Mutable (изменяемые) типы

Встроенные mutable типы:

  • list — списки
  • dict — словари
  • set — множества
  • bytearray — изменяемые байты
# List (изменяемая)
lst = [1, 2, 3]
original_id = id(lst)
lst.append(4)          # Изменяем существующий объект
print(id(lst))         # ТОТ ЖЕ адрес!
print(lst)             # [1, 2, 3, 4]

# Dict (изменяемая)
d = {"a": 1}
original_id = id(d)
d["b"] = 2            # Изменяем существующий объект
print(id(d))          # ТОТ ЖЕ адрес!
print(d)              # {"a": 1, "b": 2}

# Set (изменяемая)
s = {1, 2, 3}
original_id = id(s)
s.add(4)               # Изменяем существующий объект
print(id(s))          # ТОТ ЖЕ адрес!

Критическое различие с функциями:

def modify_list(lst):
    lst.append(100)    # Изменяем оригинальный объект!
    return lst

original = [1, 2, 3]
result = modify_list(original)
print(original)       # [1, 2, 3, 100] — ИЗМЕНИЛАСЬ!

# Это частая ошибка — побочный эффект (side effect)

Опасности mutable объектов

1. Побочные эффекты

def process_data(data):
    data["processed"] = True  # Изменяем входной dict!
    return data

original_data = {"id": 1}
result = process_data(original_data)
print(original_data)  # {"id": 1, "processed": True} — ИЗМЕНИЛАСЬ!

Решение: копирование

def process_data(data):
    # Создаём копию, чтобы не повредить оригинал
    data_copy = data.copy()  # Поверхностная копия
    # или
    data_copy = dict(data)   # Альтернатива
    # или для глубокой копии
    import copy
    data_copy = copy.deepcopy(data)
    
    data_copy["processed"] = True
    return data_copy

original_data = {"id": 1}
result = process_data(original_data)
print(original_data)  # {"id": 1} — не изменилась

2. Проблема с дефолтными параметрами

# ОПАСНЫЙ КОД!
def add_item(item, items=[]):  # Dефолтный list создаётся один раз!
    items.append(item)
    return items

result1 = add_item(1)      # [1]
result2 = add_item(2)      # [1, 2] — ожидали [2]!
result3 = add_item(3)      # [1, 2, 3]

# Все вызовы используют ОДИН И ТОТ ЖЕ list!

Правильный подход:

# ПРАВИЛЬНЫЙ КОД
def add_item(item, items=None):  # Используем None
    if items is None:
        items = []
    items.append(item)
    return items

result1 = add_item(1)      # [1]
result2 = add_item(2)      # [2]
result3 = add_item(3)      # [3]

3. Alias-проблема (совместное использование ссылок)

original = [1, 2, 3]
copy_ref = original  # Не копия, а еще одна ссылка на тот же объект!

copy_ref.append(4)
print(original)     # [1, 2, 3, 4] — ИЗМЕНИЛАСЬ!

# Правильное копирование
true_copy = original.copy()  # Поверхностная копия
true_copy.append(5)
print(original)     # [1, 2, 3, 4] — не изменилась

# Глубокая копия для вложенных структур
import copy
deep_copy = copy.deepcopy(original)

Таблица сравнения

АспектImmutableMutable
Типыint, str, tuple, frozensetlist, dict, set
ИзменяемостьНетДа
Скорость измененийМедленнее (новый объект)Быстрее (in-place)
Ключи словаряДаНет
В setДаНет
Побочные эффектыНетДа
ПотокобезопасностьЛучшеТребует синхронизации

Лучшие практики

  1. Предпочитай immutable где возможно

    • Tuple вместо list для fixed данных
    • frozenset вместо set если нужен ключ
  2. Явно копируй mutable объекты

    def safe_function(data):
        data = data.copy()  # Сразу копируем
        # теперь безопасно изменять data
    
  3. Избегай mutable дефолтных параметров

    # Плохо
    def func(x, items=[]):
    
    # Хорошо
    def func(x, items=None):
        if items is None:
            items = []
    
  4. Возвращай новые объекты вместо изменения входных

    • Это делает код предсказуемым и тестируемым