В python, языке с сильной функциональностью ООП, все является объектом. Одним из методов классификации для разделения объектов на две группы является "изменяемый или неизменяемый", который показывает, можно ли изменить объект после его создания.

Эту особенность стоит обсудить, избежать поведения модификации объектов в программе для разработчиков практически невозможно. Когда мы намереваемся изменить объект, нам нужно знать, можно ли его изменить. Или, более абстрактная проблема, которая на самом деле изменилась?

Важнейшей задачей для магазина напитков является четкое определение того, какой напиток должен быть без сахара, а какой должен быть с полным содержанием сахара, даже если все они являются напитками.

Функция id()

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

И пропускаю без раздумий :).

Но программное обеспечение, которое я разрабатывал, со временем становилось все более и более сложным, какое-то странное поведение моего кода заставило меня разобраться в расширенном жизненном цикле объектов в Python.

Как описано в Документации Python 3.8.2 для id():

Возвращает «идентификатор» объекта. Это целое число, которое гарантированно будет уникальным и постоянным для данного объекта в течение его жизни. Два объекта с непересекающимися временами жизни могут иметь одинаковое значение id().

Звучит интересно, давайте проведем несколько тестов

В приведенном выше коде строковый объект «12» был создан и назначен переменной x, затем мы просматриваем их идентификаторы.

139933797597872
139933797597872

Возможно, ваш id объекта не будет таким же, как у меня, но это не имеет значения. Нам нужно сосредоточиться на том, что переменная x хранит объект «12», на самом деле это один и тот же объект из-за одного и того же идентификатора!

Итак, если я изменю переменную x, объект «12» будет изменен, верно?

140564650636400
140564650636528

Нет, это уже не то же самое, модифицированная команда уже создает новый объект для x. Вот как работает неизменяемый объект в python: после того, как он был создан, его нельзя изменить.

Кстати, вот еще одно поучительное поведение:

а выход такой…

139904208979056
139904208094320
139904208094320
139904219321408
139904219321440
139904219321440

Похоже, что Python предпочитает повторно использовать уже созданные неизменяемые объекты (очень экономичный поступок…), поскольку они неизменяемы, нет проблем с плавающим состоянием, о которых нужно беспокоиться.

Изменяемые объекты

В отличие от неизменяемых объектов, они допускают допустимое изменение, даже если они уже были созданы. Общие типы данных, такие как list, dict и set, являются изменяемыми.

140199618200112
140199618200112
140199617680000
140199617680000
140199616712288
140199616712288

Мы можем найти идентификатор объекта без изменений после модификации, вывод показывает, что это все тот же объект. Вот почему глубокая копия для объекта обязательна, если вы не хотите загрязнять оригинал.

Вложенная структура

Давайте дадим тест на изменение списка внутри кортежа, который представляет собой изменяемый объект, встроенный в неизменяемый. Теоретически кортеж только гарантирует, что объекты, которые он содержит, не изменились, но любые изменения в самих объектах не будут запрещены.

140377141139592
140377162534280
140377141139592
140377162534280

Да, для нас допустимо обновлять изменяемый элемент внутри неизменяемого без создания нового объекта, это можно проверить по идентификатору объекта.

Звонок по ссылке

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

139937276418184
139937276418184
[1, 0]
139937276418184

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

Проблема загрязнения может быть решена путем глубокого копирования параметра:

139658206304392
139658206304456
[1]
139658206304392

Новый идентификатор объекта с объектом внутри функции, он совершенно не связан с исходным объектом, никакое загрязнение не отразится на исходном объекте.