Ветер переменных
Переменные в Python не то, чем они кажутся. А что же они тогда такое? Попробуем разобраться.
Реклама
Да, переменные - это основа основ. Если вы изучали Python хотя бы месяц, то не могли пройти мимо них. Хотя какой там месяц - о переменных обычно рассказывают на первом же занятии. А если Python - это не первый ваш язык, то вы тем более в курсе, что значит “переменная”. И всё-таки я рекомендую вам продолжить чтение в любом случае, потому что здесь не всё так просто. Но мы попробуем упростить.
Что такое переменная?
Переменная (что очевидно из названия) - это то, что можно легко изменить. Чаще всего в объяснениях мелькает сравнение переменных с коробочками. Любая коробочка - переменная, потому что можно легко изменить её содержимое. Сначала положить в неё ручку. И тогда она будет содержать ручку. Потом ручку вынуть и положить туда смартфон. Коробочка та же, а содержание изменилось. Вот и в переменную мы можем положить строку, можем список, а можем даже целую функцию. Что положили, то там и “лежит”. И если мы содержимое изменим, называться переменная будет прежним образом (коробочка та же). Чаще всего на этом объяснение и заканчивается. Всем всё понятно, расходимся. Попробуем немного усложнить.
Что значит “коробочка”, куда попадает содержимое в виде строк, списков и всего прочего, что мы туда кладём? В языке C (Си), например, коробочка (то есть переменная) - это ячейка памяти. То есть прям вот коробочка и есть. Когда мы объявляем переменную, в недрах компьютера (как на складе) резервируется ячейка памяти (коробка определенного размера), куда и попадает содержимое, которое мы в нее решили положить. Если мы содержимое коробочки решим поменять, то коробочка (ячейка памяти) в прямом смысле остаётся той же самой.
А как обстоит дело в Python?
Совсем не так. При объявлении переменной тоже резервируется ячейка памяти (коробочка), но когда мы решаем в ту же переменную записать что-то другое, мы кладем это в другую ячейку памяти (новую коробочку), хоть и называется она точно так же. Потому что то, что мы называем “переменной” в Python, - вовсе не коробочка, а бирка, которую мы на нее наклеиваем. И когда мы меняем содержимое переменной, мы не кладем в ту же коробочку (ячейку памяти) что-то новое, а переклеиваем ту же самую бирку на новую коробочку с новым содержимым.
Если говорить совсем уж строго, переменная в Python - вообще не переменная в том смысле, в котором это слово употребляется в контексте языка C. Она скорее ссылка на объект (object reference) или имя (name).
Тогда почему мы называем одним и тем же словом (“переменная”) вещи, которые в Python и других языках различаются? Для упрощения: потому что используем мы их в разных языках схожим образом. Объявил переменную, положил в неё что-нибудь - и вперед: крути циклы, отправляй в функции, меняй содержимое. Чаще всего задумываться о том, что она там означает на самом деле - коробочку или бирку на ней, не придется.
А зачем тогда вообще об этом знать?
Всё-таки этот момент нужно уяснить, чтобы разобраться с другими важными вещами, которые напрямую с ним связаны:
- разницу между оператором “==“ и ключевым словом ‘is’
- нюансы изменяемости и неизменяемости объектов
- сложности поведения некоторых переменных, которые передаются в функции в качестве параметров
- динамическую типизацию и т.д. и т.п.
Всё это мы обязательно обсудим, но как-нибудь в другой раз. А сейчас закрепим коробочно-бирочную аналогию на примере.
Пример
name = "Стэн"
print(id(name))
# 4427735920
same_name = "Стэн"
print(id(same_name))
# 4427735920
name = "Эрик"
print(id(name))
# 4428719728
Разберёмся в том, что только что произошло на наших глазах.
- Мы завели бирку (переменную) name и наклеили ее на коробку (ячейку памяти), в которую положили строку “Стэн”. Дальше мы захотели посмотреть на уникальный заводской номер этой коробки (вызвали функцию id, которая возвращает уникальный адрес ячейки памяти, в которой хранится объект).
- Далее мы завели переменную same_name и записали в нее ту же самую строку “Стэн”. Проверяем адрес ячейки и… он оказывается тем же самым. Почему? Потому что на нашем складе (в памяти) уже хранится коробка (ячейка) с таким же точно содержимым. Мы завели её на предыдущем шаге. Зачем создавать ещё одну такую же коробку для такого же содержимого, если можно просто наклеить на уже существующую коробку ещё одну бирку (нашу новую переменную)? Незачем, она будет занимать лишнее место. Точно так же "думает" и интерпретатор Python. В целях экономии памяти он проверяет, не создавалась ли уже ранее строка, которую мы пытаемся создать. И если она-таки создавалась, новая переменная привязывается к уже существующей ячейке памяти. Всегда ли так происходит? Нет, не всегда, но об этом поговорим подробнее в следующих постах. А пока запомним, что за экономией пространства на нашем складе с коробками следят довольно внимательно.
- Наконец, мы меняем содержимое нашей первой переменной name, записав туда новую строку “Эрик”. Проверяем уникальный адрес ячейки, и он теперь другой! Почему? Потому что мы просто переклеили бирку на новую коробку. В старой у нас по-прежнему лежит “Стэн”, и на неё наклеена бирка same_name…
Если это всё ещё выглядит сложно и непонятно, советую немножко поиграться с этим и самостоятельно всё “пощупать” в PyCharm или любой другой удобной IDE. Заранее предупреждаю: если вы будете экспериментировать с другими типами данных, результаты могут отличаться, но об этом подробнее поговорим в другой раз.
Все статьи