Изменяемые и неизменяемые типы данных

Все данные делятся на два типа: неизменяемые или изменяемые. Очевидно, что первые в конце программы выглядят точно так же, как и в начале. К ним относятся числа, строки и кортежи. С другой стороны, списки и словари могут измениться в процессе работы. Например, в них может стать больше или меньше элементов.

Когда данные передаются в функцию, способ их обработки зависит от типа. Например, для неизменяемых чисел создается независимая копия. Следовательно, любое преобразование внутри функции не повлияет на исходное число. И наоборот, вместо изменяемого списка передается указатель на то место в памяти, где он хранится. Таким образом, все трансформации повлияют на внешний объект.

def foo(a=[]):
    a.append(1)
    print(a)

foo()
foo()
foo()

Первый вызов функции foo предсказуемо выведет список, состоящий из одного элемента 1. Но если вы ожидаете такого же результата от второго и третьего вызовов, то будете удивлены. На самом деле вывод будет следующим:

[1]
[1, 1]
[1, 1, 1]

Так происходит потому, что при первом вызове в памяти создается пустой список a. Именно к нему функция будет обращаться и дальше, если не получит собственный аргумент. Так как список не копируется, а передается по ссылке, он будет изменяться.

Эту концепцию важно понять, чтобы не допускать подобных ошибок. Их сложно отследить в процессе отладки, поэтому приходится тратить много времени на поиск проблемы.