Изменяемость, копирование и объектная модель
Имена, объекты, идентичность и почему копирование никогда не бывает таким простым, как кажется
is и ==, ловушка изменяемого аргумента по умолчанию и разница между поверхностным и глубоким копированием перестанут быть сюрпризами и станут предсказуемыми следствиями одной модели.
- объяснить разницу между именем, объектом и значением;
- точно рассуждать об алиасинге и о том, когда два имени разделяют один объект;
- правильно выбирать между
is(идентичность) и==(равенство); - распознавать и исправлять ловушку изменяемого аргумента по умолчанию;
- выбирать подходящий инструмент среди присваивания,
copy.copyиcopy.deepcopy.
1. Имена привязываются к объектам, а не хранят их
Каждое значение в Python — это объект с тремя атрибутами: идентичностью (его адрес, который показывает id()), типом и данными, которые он несёт. Переменная — это просто имя в некотором пространстве имён, которое ссылается на один из этих объектов. Когда вы пишете b = a, Python не дублирует объект, на который указывает a; он делает так, что b ссылается на тот же самый объект. Теперь оба имени находятся поверх одного объекта.
a = [1, 2, 3]
b = a # b ссылается на ТОТ ЖЕ объект-список, а не на копию
b.append(4)
print(a) # [1, 2, 3, 4] -- a "изменился", ведь список один
print(id(a) == id(b)) # True -- одинаковая идентичность
Это не особый случай для списков. Так работает любое присваивание — для целых чисел, строк, функций и экземпляров одинаково. Разница между «типами-значениями» и «ссылочными типами», которую вы ощущаете в других языках, здесь отсутствует: всё является ссылкой на объект. Различается лишь то, можно ли изменить объект после создания.