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

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

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

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

Что такое изменчивость?

С точки зрения технического программирования, изменяемый объект — это объект, состояние которого может быть изменено после его определения. Противоположностью изменяемого объекта является неизменяемый объект, состояние которого нельзя изменить после того, как он был первоначально определен.

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

Давайте рассмотрим конкретный пример разницы между изменчивостью и неизменностью. Скажем, я определяю список песен. Если я хочу изменить список, заменив одну из песен, это прекрасно, потому что списки изменяемы:

>>> my_songs = ["All Too Well", "No Tears Left to Cry", "Cruel Summer"]
>>> my_songs[1] = "Champagne Problems"
>>> my_songs
['All Too Well', 'Champagne Problems', 'Cruel Summer']

Напротив, предположим, что я определяю строку, которая является неизменяемым объектом, но случайно написал ее с ошибкой. Если я затем попытаюсь изменить строку, Python жалуется:

>>> my_song = "All Too Wall"
>>> my_song[9] = 'e'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

После определения строки ее содержимое нельзя изменить. Мне нужно будет определить новую строку, если я хочу исправить правописание.

Если вы подумаете о функциональности списков и словарей, вы поймете, что все это время пользовались их изменчивостью, даже если вы этого не осознавали. Такие функции, как append, extend и update, изменяют список или словарь, на который они ссылаются, и могут быть очень полезны для отслеживания данных или выполнения определенной задачи программирования.

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

Подробный пример того, почему это важно

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

Первая функция добавляет число 6 к целому числу, а вторая функция добавляет число 6 к списку:

# Add 6 to a number
def addnumto_number(num):
     num = num + 6
# Append 6 to the end of a list
def appendnumto_list(lst):
     lst.append(6)

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

>>> num = 10
>>> addnumto_number(num)
>>> print(num) # num remains unchanged
10
>>> list_num = [10]
>>> appendnumto_list(list_num)
>>> print(list_num) # list_num, however, changes
[10, 6]

Лично я нашел это увлекательным, когда впервые узнал об этом. Переменная num остается неизменной, а переменная list_num изменяется. Почему это происходит? Это связано с тем, как изменяемые объекты передаются в функцию. Это легче увидеть визуально.

Ниже я скопировал три изображения из Python Tutor, отличного онлайн-инструмента, который может помочь вам визуализировать ваш код Python. Они показывают, что происходит под капотом до, во время и после того, как я вызову функцию add_6_to_num.

Ключевым наблюдением здесь является то, что при вызове add_6_to_num создается копия переменной num , и именно эта копия изменяется внутри функции. Но начальная переменная num, которую мы определяем, остается неизменной вне функции. Это верно для всех неизменяемых объектов: когда они передаются в функцию, Python создает их копию.

Теперь давайте посмотрим на соответствующие три диаграммы для списка:

Видите принципиальную разницу? Поскольку списки изменяемы, Python не создает их копии при передаче в функцию. Таким образом, выше ссылка (представленная стрелкой) на тот же список передается в функцию append_6_to_list. Переменная внутри и вне функции ссылается на один и тот же список, поэтому при ее изменении внутри функции значение переменной num_list вне функции также меняется.

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

Последние мысли

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

Списки и словари являются огромной частью науки о данных в Python — они обе представляют собой структуры данных, которые широко используются в модуле обработки данных Python Pandas. В результате важно понимать, как Python обрабатывает эти объекты при написании кода, который использует их и полагается на них. Знание концепций, изложенных в этой статье, было полезно для меня, и я надеюсь, что это может быть полезно и для вас.

До следующего раза, ребята!

Хотите преуспеть в Python? Получите эксклюзивный бесплатный доступ к моим простым и понятным руководствам здесь.

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