Особенности переменных языка Python

Python – скриптовый язык, получивший широкую популярность за счет простоты синтаксиса, скорости написания програм и лёгкой интеграции скриптов. Однако, люди перешедшие на питон с императивных языков, таких как С, например, могут столкнуться с некоторыми неожиданностями, с которыми столкнулся и я.

Однажды я работал с загрузчиком прошивки на микроконтроллере и мне была поставлена задача написать программу для взаимодействия с этим загрузчиком. Не заморачиваясь я решил писать её на Python. Особенности протокола взаимодействия загрузчика с внешним миром таковы, что каждая посылка начинается с 3-байтового заголовка (например: 0x01 0x02 0x03). Однако, в некоторых случаях заголовок становится двойным т.е. 0x01 0x02 0x03 0x01 0x02 0x03. Записав одинарный заголовок в виде списка, я добавил условие удваивания заголовка, после которого записал такой код:

# "Удивительный" код
# Заголовок
sHeader = [0x01, 0x02, 0x03]
# Формируем двойной заголовок
dHeader = sHeader
for i in sHeader:
    dHeader.append(i)

Логика проста – копируем массив, а затем последовательно по одному элементу добавляем далее ещё одну его копию. Но что-то пошло не так!

И всё зависло очень надолго!

В императивных языках программирования, например Си, каждая переменные представляют собой некий контейнер – куда помещаются данные. А вот в объектно-ориентированных языках слово переменная уже не столь корректно, точнее называть их “именами”. Почему? Давайте рассмотрим на примерах!

Использование “переменных”

В таких языках переменная представляет собой некий контейнер и следующий код можно представить таким образом:

int a = 1;

Теперь контейнер a содержит число 1. Присвоим переменной a число 2.

a = 2;

Теперь контейнер a содержит число 2. Если мы присвоим переменной b значение переменной a, то будет создана копия значения переменной a и помещена в контейнер b.

int b = a;

Теперь b это вторая коробка с копией числа 2.

Использование “имен”

В Python “имя” или “идентификатор” можно представить в виде именной бирки.

a = 1

Здесь числовой объект 1 имеет “бирку” с именем а. При попытке переопределить значение а, мы просто “перевесим” бирку на другой объект:

a = 2

Теперь бирка а указывает на другой объект, а объект 1 не имеет бирок. Он может продолжить своё существование, но мы не можем больше получить к нему доступ через бирку а. (Когда какой-либо объект не имеет “бирок” (указателей на себя) он удаляется из памяти).

Теперь когда мы возьмём переменную b и присвоим ей значение переменной а, то фактически, мы повесим на объект 2 бирку b.

b = a

Теперь b это просто ещё одно имя указывающее на объект 2. Таково отличие “идентификаторов” от переменных, хотя даже в Python их традиционно называют “переменные”, не смотря на иной принцип работы.

А теперь возвращаясь к моему коду в начале статьи становится понятным, почему python зависал: перебирая в цикле каждый элемент в каждой итерации я добавлял элемент в конец списка и сдвигался на 1 шаг к концу массива, таким образом, оставаясь на равном удалении от конца массива, который бесконечно разрастался из-за того, что обе переменных указывали на один и тот же объект.

А вы используете скриптовые языки в своей практике? Какие?


Статья основана на материале: https://doc.sagemath.org/html/en/thematic_tutorials/tutorial-objects-and-classes.html