Книга: Как устроен Python. Гид для разработчиков, программистов и интересующихся
Назад: 5. Запись и чтение данных
Дальше: 7. Подробнее об объектах

6. Переменные

Итак, вы научились запускать программы в интерпретаторе (или REPL) и в командной строке. Теперь можно осваивать основы программирования. Переменные — основные структурные элементы компьютерных программ.

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

6.1. Изменение и состояние

В программировании существует две важные концепции: состояния и изменения. Состояние связано с цифровым представлением модели. Например, если вы хотите моделировать лампочку, можно хранить в программе ее текущий статус — включена лампочка или нет? Среди других интересных вариантов состояния можно хранить тип лампочки (люминесцентная, лампа накаливания), потребляемую мощность, размер, возможность регулировки яркости и т.д.

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

Какое отношение все это имеет к переменным? Вспомните, что в Python нет ничего, кроме объектов. Объекты обладают состоянием, которое может изменяться. Для хранения информации об объектах используются переменные.

Когда в программе появляются объекты с состоянием, которое может изменяться, перед вами открывается целый мир новых возможностей. В программе можно смоделировать практически все, что угодно, — если вы сможете определить, каким состоянием должна обладать модель и какие действия (или изменения) к ней должны применяться.

6.2. Переменные Python как метки

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

>>> status = "off"

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

Этот объект обладает рядом интересных свойств. Во-первых, у него есть идентификатор. Можно считать, что идентификатор указывает на место, в котором Python хранит этот объект в памяти. У объекта также имеется тип (в данном случае это строка). Наконец, у объекта есть значение — здесь оно состоит из символов off, так как это строка.

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

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

ПРИМЕЧАНИЕ

Если вы захотите узнать значение счетчика ссылок для объекта, вызовите для него функцию sys.getrefcount:

>>> import sys

>>> names = []

>>> sys.getrefcount(names)

2

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

«…Возвращает счетчик ссылок объекта. Возвращаемое значение обычно на 1 выше ожидаемого, потому что оно включает (временную) ссылку для аргумента getrefcount()».

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

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

Для пущей наглядности попробуйте теперь снова прочитать код — на этот раз слева направо. Status — переменная, с которой связывается объект, созданный за нас Python. У этого объекта есть тип (строка), и он содержит значение "off".

6.3. Бирки

У моего дедушки было животноводческое ранчо, поэтому я приведу некомпьютерную аналогию. Любому владельцу ранчо необходим хороший гуртовщик, который следит за скотом (то есть за хозяйскими капитало­вложениями).

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

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

Подобно тому, как скотоводы помечают бирками своих животных, программисты создают переменные для отслеживания данных. Еще раз взгляните на наш пример:

>>> status = "off"

Эта команда приказывает Python создать строку, содержащую текст off. Программа создает переменную с именем status и связывает ее с этой строкой. Позднее, когда вам потребуется узнать информацию состояния, можно приказать программе вывести ее на экран — вот так:

>>> print(status)

Off

513716.png 

Рис. 6.1. Два этапа присваивания литерала. Сначала Python создает объект. У этого объекта есть значение “off”, тип (строка) и идентификатор (местонахождение объекта в памяти). После того как объект будет создан, Python ищет переменную с именем status. Если такая переменная существует, Python изменяет объект, на который указывает эта переменная; в противном случае Python создает переменную и связывает ее с объектом

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

>>> "120 watt"

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

>>> wattage = "120 watt"

Позднее в своей программе вы можете обратиться к переменной wattage, вывести ее значение, даже присвоить его другой переменной или присвоить wattage новое значение (допустим, ваша лампа накаливания разбилась и вы заменили ее светодиодной):

>>> incandescent = wattage

>>> wattage = "25 watt"

>>> print(incandescent, wattage)

120 watt 25 watt

Управление состоянием — один из основных аспектов программирования, а переменные — один из механизмов этого управления.

6.4. Повторное связывание переменных

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

>>> num = 400

>>> num = '400' # теперь num содержит строку

В этом примере переменная num сначала была связана с целым числом, но потом программа связала ее со строкой.

ПРИМЕЧАНИЕ

Для переменной тип неважен. В языке Python типом обладает объект, а не переменная.

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

513736.png 

Рис. 6.2. Повторное связывание переменных. Переменная может быть заново связана с любым типом; Python не пытается этому как-то помешать и не выдает предупреждений. Если с объектом не связана ни одна переменная, Python уничтожает этот объект в процессе уборки мусора

СОВЕТ

Это один из тех случаев, когда Python позволяет вам что-то сделать, но это вовсе не означает, что это стоит делать в реальной жизни. Да, переменную можно заново связать с другим типом, но не нужно. Изменение типа переменной затруднит чтение кода. Кроме того, программа только запутает других разработчиков, которые используют ваш код. Не используйте одну переменную для работы со значениями разных типов!

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

6.5. Имена переменных

В Python имена переменных не выбираются полностью произвольно: существуют конкретные соглашения, которые выполняются большинством программистов. Одни из этих соглашений обязательны к выполнению, другие — нет. Например, интерпретатор требует, чтобы имя переменной не совпадало с ключевым словом языка. Скажем, break — ключевое слово, поэтому ваша переменная не может называться break. При попытке использовать break как имя переменной, вы получите синтаксическую ошибку. И хотя следующий код выглядит вполне нормально, Python сообщит об ошибке:

>>> break = 'foo'

File "<stdin>", line 1

break = 'foo'

^

SyntaxError: invalid syntax

Если вы столкнетесь с синтаксической ошибкой (SyntaxError) в совершенно нормальном на первый взгляд коде Python, убедитесь в том, что имя переменной не совпадает с ключевым словом.

ПРИМЕЧАНИЕ

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

Модуль keyword содержит атрибут kwlist со списком всех ключевых слов Python:

>>> import keyword

>>> print(keyword.kwlist)

['False', 'None', 'True', 'and', 'as', 'assert',

'break', 'class', 'continue', 'def', 'del', 'elif',

'else', 'except', 'finally', 'for', 'from', 'global',

'if', 'import', 'in', 'is', 'lambda', 'nonlocal',

'not', 'or', 'pass', 'raise', 'return', 'try',

'while', 'with', 'yield']

Также для просмотра ключевых слов в REPL можно вызвать метод help(). Он переводит REPL в режим справочной информации, в котором можно вводить команды для получения справки (в отличие от команд Python). Введите keywords и нажмите Enter. Если ввести любое ключевое слово, программа выведет документацию и сопутствующие разделы справки. Чтобы выйти из режима справки, просто нажмите Enter.

6.6. Дополнительные рекомендации по назначению имен

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

• имена переменных должны состоять их символов нижнего регистра;

• слова должны разделяться символом подчеркивания;

• имена переменных не могут начинаться с цифр;

• имена переменных не могут переопределять встроенные функции.

Несколько примеров имен переменных — как хороших, так и плохих:

>>> good = 4

>>> bAd = 5 # плохо - верхний регистр

>>> a_longer_variable = 6

 

# Не рекомендуется

>>> badLongerVariable = 7

 

# Плохо - начинается с цифры

>>> 3rd_bad_variable = 8

File "<stdin>", line 1

3rd_bad_variable = 8

^

SyntaxError: invalid syntax

 

# Плохо - ключевое слово

>>> for = 4

File "<stdin>", line 1

for = 4

^

SyntaxError: invalid syntax

 

# Плохо - встроенная функция

>>> compile = 5

СОВЕТ

Правила и соглашения назначения имен в Python перечислены в документе «PEP 8 — Style Guide for Python Code». Сокращение PEP означает «Python Enhancement Proposal», то есть «Предложение по улучшению Python» — так называется процесс документирования функции, усовершенствования или передовой практики для Python. Документы PEP доступны на сайте Python.

ПРИМЕЧАНИЕ

Хотя Python не разрешает использовать ключевые слова в качестве имен переменных, встроенные имена могут использоваться как переменные. Встроенные имена (built-ins) — имена функций, классов или переменных, которые Python загружает автоматически, чтобы вам было проще обращаться к ним. В отличие от ключевых слов, Python позволяет использовать встроенное имя в качестве названия переменной без малейших возражений. Тем не менее поступать так не рекомендуется — это считается признаком плохого стиля.

Использование встроенного имени в качестве названия переменной приводит к замещению встроенного имени. Новое имя переменной не позволит получить доступ к исходному встроенному имени. Фактически вы берете встроенную переменную и заимствуете ее для собственного использования. В результате доступ к исходному встроенному имени будет возможен только через модуль __builtins__. Гораздо лучше с самого начала избегать замещения.

Ниже перечислены встроенные имена Python, которые не следует использовать в качестве имен переменных:

>>> dir(__builtins__)

 

['ArithmeticError', 'AssertionError',

'AttributeError', 'BaseException',

'BlockingIOError', 'BrokenPipeError',

'BufferError', 'BytesWarning', 'ChildProcessError',

'ConnectionAbortedError', 'ConnectionError',

'ConnectionRefusedError', 'ConnectionResetError',

'DeprecationWarning', 'EOFError', 'Ellipsis',

'EnvironmentError', 'Exception', 'False',

'FileExistsError', 'FileNotFoundError',

'FloatingPointError', 'FutureWarning',

'GeneratorExit', 'IOError', 'ImportError',

'ImportWarning', 'IndentationError', 'IndexError',

'InterruptedError', 'IsADirectoryError',

'KeyError', 'KeyboardInterrupt', 'LookupError',

'MemoryError', 'NameError', 'None',

'NotADirectoryError', 'NotImplemented',

'NotImplementedError', 'OSError', 'OverflowError',

'PendingDeprecationWarning', 'PermissionError',

'ProcessLookupError', 'RecursionError',

'ReferenceError', 'ResourceWarning',

'RuntimeError', 'RuntimeWarning',

'StopAsyncIteration', 'StopIteration',

'SyntaxError', 'SyntaxWarning', 'SystemError',

'SystemExit', 'TabError', 'TimeoutError', 'True',

'TypeError', 'UnboundLocalError',

'UnicodeDecodeError', 'UnicodeEncodeError',

'UnicodeError', 'UnicodeTranslateError',

'UnicodeWarning', 'UserWarning', 'ValueError',

'Warning', 'ZeroDivisionError', '_',

'__build_class__', '__debug__', '__doc__',

'__import__', '__loader__', '__name__',

'__package__', '__spec__', 'abs', 'all', 'any',

'ascii', 'bin', 'bool', 'bytearray', 'bytes',

'callable', 'chr', 'classmethod', 'compile',

'complex', 'copyright', 'credits', 'delattr',

'dict', 'dir', 'divmod', 'enumerate', 'eval',

'exec', 'exit', 'filter', 'float', 'format',

'frozenset', 'getattr', 'globals', 'hasattr',

'hash', 'help', 'hex', 'id', 'input', 'int',

'isinstance', 'issubclass', 'iter', 'len',

'license', 'list', 'locals', 'map', 'max',

'memoryview', 'min', 'next', 'object', 'oct',

'open', 'ord', 'pow', 'print', 'property', 'quit',

'range', 'repr', 'reversed', 'round', 'set',

'setattr', 'slice', 'sorted', 'staticmethod',

'str', 'sum', 'super', 'tuple', 'type', 'vars',

'zip']

СОВЕТ

Несколько встроенных имен, которые выглядят особенно соблазнительно в качестве имен переменных: dict, id, list, min, max, open, range, str, sum и type.

6.7. Итоги

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

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

6.8. Упражнения

1. Создайте переменную pi, которая указывает на приближенное значение числа π. Создайте переменную r для представления радиуса круга; эта переменная должна быть равна 10. Вычислите площадь круга (π × квадрат радиуса). Умножение выполняется оператором *, а возведение числа в квадрат — оператором **. Например, результат 3**2 равен 9.

2. Создайте переменную b, которая указывает на ширину прямоугольника со значением 10. Создайте переменную h, которая указывает на высоту прямоугольника со значением 2. Вычислите длину периметра прямоугольника. Измените ширину, присвоив ей значение 6, и снова вычислите периметр.

/

Назад: 5. Запись и чтение данных
Дальше: 7. Подробнее об объектах