• en
  • Language: ru
  • Documentation version: latest

9. Мутация

Мутабельный и неизменяемый типы данных в Python вызывают много головной боли у начинающих программистов. Простыми словами, mutable означает «способный к изменению», а immutable - «постоянный». Хотите, чтобы у вас голова пошла кругом? Рассмотрим следующий пример:

foo = ['hi']
print(foo)
# Output: ['hi']

bar = foo
bar += ['bye']
print(foo)
# Output: ['hi', 'bye']

Что только что произошло? Мы этого не ожидали! Мы ожидали чего-то подобного:

foo = ['hi']
print(foo)
# Output: ['hi']

bar = foo
bar += ['bye']

print(foo)
# Expected Output: ['hi']
# Output: ['hi', 'bye']

print(bar)
# Output: ['hi', 'bye']

Это не ошибка. Это мутабельность в действии. Всякий раз, когда вы присваиваете переменную другой переменной с изменяемым типом данных, любые изменения данных отражаются в обеих переменных. Новая переменная - это просто псевдоним старой переменной. Это справедливо только для изменяемых типов данных. Здесь есть одна загвоздка, связанная с функциями и изменяемыми типами данных:

def add_to(num, target=[]):
    target.append(num)
    return target

add_to(1)
# Output: [1]

add_to(2)
# Output: [1, 2]

add_to(3)
# Output: [1, 2, 3]

Возможно, вы ожидали, что он будет вести себя по-другому. Возможно, вы ожидали, что при вызове add_to вот так будет создан свежий список:

def add_to(num, target=[]):
    target.append(num)
    return target

add_to(1)
# Output: [1]

add_to(2)
# Output: [2]

add_to(3)
# Output: [3]

И снова причиной этой боли является изменяемость списков. В Python аргументы по умолчанию оцениваются один раз при определении функции, а не при каждом ее вызове. Вы никогда не должны определять аргументы по умолчанию с изменяемым типом, если не знаете, что делаете. Вы должны сделать что-то вроде этого:

def add_to(element, target=None):
    if target is None:
        target = []
    target.append(element)
    return target

Теперь каждый раз, когда вы вызываете функцию без аргумента target, создается новый список. Например:

add_to(42)
# Output: [42]

add_to(42)
# Output: [42]

add_to(42)
# Output: [42]