Изменяемые и неизменяемые типы данных (Я не умничаю, просто этот момент я сам для себя не усвоил, а он мне кажется очень важным)
Все данные относятся к одному из двух типов – неизменяемые или изменяемые. Очевидно, что первые в конце программы выглядят точно так же, как и в начале. К ним относятся числа, строки и кортежи. С другой стороны, списки и словари могут измениться в процессе работы. Например, добавятся или удалятся элементы.
Когда данные передаются в функцию, способ их обработки зависит от типа. Например, для неизменяемых чисел создается независимая копия. Следовательно, любое преобразование внутри функции не повлияет на исходное число. И наоборот, вместо изменяемого списка передается указатель на то место в памяти, где он хранится. Таким образом, все трансформации повлияют на внешний объект.
def foo(a=[]):
a.append(1)
print(a)
foo()
foo()
foo()
1
2
3
4
5
6
7
def foo(a=[]):
a.append(1)
print(a)
foo()
foo()
foo()
Первый вызов функции foo предсказуемо выведет список, состоящий из одного элемента 1. Однако если вы ожидаете такого же результата от второго и третьего вызовов, то будете удивлены. На самом деле, вывод будет следующим:
# [1]
# [1, 1]
# [1, 1, 1]
1
2
3
# [1]
# [1, 1]
# [1, 1, 1]
Так происходит, потому что при первом вызове в памяти создается пустой список a. Именно к нему функция будет обращаться и дальше, если не получит собственный аргумент. Так как список не копируется, а передается по ссылке, он будет изменяться.
Эту концепцию важно понять, чтобы не допускать подобных ошибок. Их сложно отследить в процессе отладки, поэтому приходится тратить много времени на поиск проблемы.