Содержание

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

Операторы Python

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

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

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

>>> print('Hello, World!')
Hello, World!

>>> x = [1, 2, 3]
>>> print(x[1:2])
[2]

Примечание: во многих примерах REPL, которые вы видели, оператор часто просто состоял из выражения, набранного непосредственно в командной строке >>>, для которого интерпретатор отображает значение:

>>> 'foobar'[2:5]
'oba'

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

Продолжение строки

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

>>> person1_age = 42
>>> person2_age = 16
>>> person3_age = 71

>>> someone_is_of_working_age = (person1_age >= 18 and person1_age <= 65) or (person2_age >= 18 and person2_age <= 65) or (person3_age >= 18 and person3_age <= 65)
>>> someone_is_of_working_age
True

Или, возможно, вы определяете длинный вложенный список:

>>> a = [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20], [21, 22, 23, 24, 25] ]
>>> a
[ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20], [21, 22, 23, 24, 25] ]

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

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

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

Примечание. Руководство по стилю для кода Python также называется PEP 8. PEP расшифровывается как Python Enhancement Proposal. PEP — это документы, содержащие подробную информацию о функциях, стандартах, проблемах проектирования, общих рекомендациях и информацию, относящуюся к Python.

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

>>> someone_is_of_working_age = person1_age >= 18 and person1_age <= 65 or
SyntaxError: invalid syntax

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

Неявное продолжение строки

Это более простой метод продолжения строки, который предпочтительнее в соответствии с PEP 8.

Любой оператор, содержащий открытые круглые скобки (‘(‘), квадратные скобки (‘[‘) или фигурные скобки (‘{‘), считается неполным, пока не встретятся все соответствующие круглые скобки, квадратные скобки и фигурные скобки. До тех пор оператор может неявно продолжаться построчно, не вызывая ошибки.

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

>>> a = [
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20],
[21, 22, 23, 24, 25]
]

>>> a
[ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15],
[16, 17, 18, 19, 20], [21, 22, 23, 24, 25] ]

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

>>> someone_is_of_working_age = (
(person1_age >= 18 and person1_age <= 65)
or (person2_age >= 18 and person2_age <= 65)
or (person3_age >= 18 and person3_age <= 65)
)

>>> someone_is_of_working_age
True

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

Круглые скобки:

Группировка выражений

>>> x = (
1 + 2
+ 3 + 4
+ 5 + 6
)
>>> x
21

Вызов функции

>>> print(
'foo',
'bar',
'baz'
)
foo bar baz

Вызов метода

>>> 'abc'.center(
9,
'-'
)
'---abc---'

Определение кортежа

>>> t = (
'a', 'b',
'c', 'd'
)

Фигурные скобки:

Определение словаря

>>> d = {
'a': 1,
'b': 2
}

Определение множества

>>> x1 = {
'foo',
'bar',
'baz'
}

Квадратные скобки:

Определение списка

>>> a = [
'foo', 'bar',
'baz', 'qux'
]

Индексирование

>>> a[
1
]
'bar'

Нарезка

>>> a[
1:2
]
['bar']

Ссылка на ключ словаря

>>> d[
'b'
]
2

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

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

>>> a = [
    [
        ['foo', 'bar'],
        [1, 2, 3]
    ],
    {1, 3, 5},
    {
        'a': 1,
        'b': 2
    }
]

>>> a
[ [ ['foo', 'bar'], [1, 2, 3] ], {1, 3, 5}, {'a': 1, 'b': 2}]

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

Явное продолжение строки

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

Обычно символ новой строки (который появляется при нажатии клавиши Enter на клавиатуре) обозначает конец строки. Если к этому времени инструкция не будет завершена, Python выдаст исключение SyntaxError:

>>> s =
  File "<stdin>", line 1
    s =
      ^
SyntaxError: invalid syntax

>>> x = 1 + 2 +
  File "<stdin>", line 1
    x = 1 + 2 +
              ^
SyntaxError: invalid syntax

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

>>> s = \
... 'Hello, World!'
>>> s
'Hello, World!'

>>> x = 1 + 2 \
+ 3 + 4 \
+ 5 + 6
>>> x
21

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

>>> # Вы этого не видите, но после \ здесь стоит пробел:
>>> s = \
  File "<stdin>", line 1
    s = \
         ^
SyntaxError: unexpected character after line continuation character

Опять же, PEP 8 рекомендует использовать явное продолжение строки только тогда, когда неявное продолжение строки невозможно.

Несколько операторов в строке

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

>>> x = 1; y = 2; z = 3
>>> print(x); print(y); print(z)
1
2
3

Стилистически это обычно не одобряется, а PEP 8 категорически не одобряет этого. Могут быть ситуации, когда это улучшает читаемость, но обычно этого не происходит. На самом деле в этом часто нет необходимости. Следующие операторы функционально эквивалентны приведенному выше примеру, но будут считаться более типичным кодом Python:

>>> x, y, z = 1, 2, 3
>>> print(x, y, z, sep='\n')
1
2
3

Термин Pythonic (питонический) относится к коду, который следует общепринятым общим рекомендациям по удобной читаемости и «лучшему» использованию идиоматического Python. Когда кто-то говорит, что код не является Pythonic (питоническим), они подразумевают, что он не выражает намерений программиста, в отличие от Python. Таким образом, код, вероятно, не так читаем, как для тех, кто свободно владеет Python.

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

Комментарии

В Python символ решетки (#) обозначает комментарий. Интерпретатор проигнорирует все, от символа решетки до конца этой строки:

>>> a = ['foo', 'bar', 'baz']  # Я комментарий.
>>> a
['foo', 'bar', 'baz']

Если первый непробельный символ в строке является хеш-символом, вся строка фактически игнорируется:

>>> # Я комментарий.
>>>     # Я тоже. 

Естественно, хэш-символ в строковом литерале защищен и не обозначает комментарий:

>>> a = 'foobar # Я *не* комментарий.'
>>> a
'foobar # Я *не* комментарий.'

Комментарии дают вам возможность добавлять пояснительные детали к вашему коду:

>>> # Вычислить и отобразить площадь круга.

>>> pi = 3.1415926536
>>> r = 12.35

>>> area = pi * (r ** 2)

>>> print('Площадь круга с радиусом', r, 'равна', area)
Площадь круга с радиусом 12.35 равна 479.163565508706

До сих пор ваш код Python состоял в основном из коротких изолированных сеансов REPL. В этом случае необходимость в комментариях минимальна. Со временем вы будете разрабатывать более крупные приложения, содержащиеся в нескольких файлах сценариев, и комментарии будут становиться все более и более важными.

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

Комментарии могут быть включены в неявное продолжение строки:

>>> x = (1 + 2           # Я комментарий.
+ 3 + 4         # Я тоже.
+ 5 + 6)
>>> x
21

>>> a = [
'foo', 'bar',    # И я тоже.
'baz', 'qux'
]
>>> a
['foo', 'bar', 'baz', 'qux']

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

>>> x = 1 + 2 + \   # Я хотел бы быть комментарием, но это не так.
SyntaxError: unexpected character after line continuation character

Многие языки программирования предоставляют синтаксис для многострочных комментариев (также называемых блочными комментариями). Например, в C и Java комментарии разделяются токенами /* и */. Текст, содержащийся в этих разделителях, может занимать несколько строк:

/*
[Это не Python!]

Инициализируйте значение радиуса круга.

Затем вычислите площадь круга
и выведите результат на консоль.
*/

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

>>> # Инициализируйте значение радиуса круга.
>>> #
>>> # Затем вычислите площадь круга
>>> # и выведите результат на консоль.

>>> pi = 3.1415926536
>>> r = 12.35

>>> area = pi * (r ** 2)

>>> print('Площадь круга с радиусом', r, 'равна', area)
Площадь круга с радиусом 12.35 равна 479.163565508706

Однако для кода в файле сценария технически есть альтернатива.

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

Рассмотрим этот файл сценария foo.py:

"""Инициализируйте значение радиуса круга.

Затем вычислите площадь круга
и выведите результат на консоль.
"""

pi = 3.1415926536
r = 12.35

area = pi * (r ** 2)

print('Площадь круга с радиусом', r, 'равна', area)

При запуске этого скрипта вывод выглядит следующим образом:

C:\Users\john\Documents\Python\doc>python foo.py
Площадь круга с радиусом 12.35 равна 479.163565508706

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

Хотя он работает (и когда-то был предложен самим Гвидо в качестве совета по программированию на Python), PEP 8 на самом деле не рекомендует его. Причина этого, по-видимому, кроется в специальной конструкции Python, называемой docstring (строка документации). Строка документации — это специальный комментарий в начале пользовательской функции, который документирует поведение функции. Строки документа обычно указываются как встроенные комментарии в тройных кавычках, поэтому PEP 8 рекомендует обозначать другие блочные комментарии в коде Python обычным способом с символом решетки в начале каждой строки.

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

Пробел

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

Обычно токены отделяются друг от друга пробелами, которые используются для улучшения читаемости. Наиболее распространены следующие пробелы:

Символ Код ASCII Буквальное выражение
space 32 (0x20) ' '
tab 9 (0x9) '\t'
newline 10 (0xa) '\n'

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

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

>>> x=3;y=12
>>> x+y
15

>>> (x==3)and(x>> a=['foo','bar','baz']
>>> a
['foo', 'bar', 'baz']

>>> d={'foo':3,'bar':4}
>>> d
{'foo': 3, 'bar': 4}

>>> x,y,z='foo',14,21.1
>>> (x,y,z)
('foo', 14, 21.1)

>>> z='foo'"bar"'baz'#Комментарий
>>> z
'foobarbaz'

В каждом из перечисленных выше операторов вообще нет пробелов, и интерпретатор отлично с ними справляется. Это не значит, что вы должны их так писать. Разумное использование пробелов почти всегда улучшает читаемость, и ваш код обычно должен включать их. Сравните следующие фрагменты кода:

>>> value1=100
>>> value2=200
>>> v=(value1>=0)and(value1<value2)

и
 

>>> value1 = 100
>>> value2 = 200
>>> v = (value1 >= 0) and (value1 < value2)

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

Примечание. Вы можете сопоставить строковые литералы с пробелами или без них:

>>> s = "foo"'bar''''baz'''
>>> s
'foobarbaz'

>>> s = 'foo' "bar" '''baz'''
>>> s
'foobarbaz'

Эффект конкатенации такой же, как если бы вы использовали оператор +.

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

Например, в следующем случае требуется пробел для отделения идентификатора s от ключевого слова in:

>>> s = 'bar'

>>> s in ['foo', 'bar', 'baz']
True

>>> sin ['foo', 'bar', 'baz']
Traceback (most recent call last):
  File "<pyshell#25>", line 1, in <module>
    sin ['foo', 'bar', 'baz']
NameError: name 'sin' is not defined

Вот пример, в котором требуется пробел, чтобы различать идентификатор y и числовую константу 20:

>>> y is 20
False

>>> y is20
SyntaxError: invalid syntax

В этом примере требуется пробел между двумя ключевыми словами:

>>> 'qux' not in ['foo', 'bar', 'baz']
True

>>> 'qux' notin ['foo', 'bar', 'baz']
SyntaxError: invalid syntax

Совместное использование идентификаторов или ключевых слов вводит интерпретатора в заблуждение, заставляя думать, что вы имеете в виду другой токен, чем предполагалось: sin, is20 и notin в приведенных выше примерах.

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

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

Глубокое погружение: Фортран и пробелы

Самые ранние версии Фортрана, одного из первых созданных языков программирования, были разработаны таким образом, что все пробелы полностью игнорировались. Кроме того, пробелы могут быть включены или опущены практически где угодно — между идентификаторами и зарезервированными словами и даже между идентификаторами и зарезервированными словами.

Например, если ваш код на Фортране содержит переменную с именем total, любое из следующих утверждений будет допустимым для присвоения ей значения 50:

total = 50
to tal = 50
t o t a l=5 0

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

Рассмотрим эту историю из НАСА 1960-х годов. Программа вычисления орбиты Центра управления полетами, написанная на Фортране, должна была содержать следующую строку кода:

DO 10 I = 1,100

На диалекте Фортрана, который использовался НАСА в то время, показанный код представляет собой цикл, конструкцию, которая выполняет итерацию по основной части кода.

К сожалению, вместо этого в программе оказалась эта строка кода:

DO 10 I = 1.100

Если вам трудно увидеть разницу, не расстраивайтесь. Программисту НАСА потребовалось несколько недель, чтобы заметить, что вместо запятой между 1 и 100 стоит точка. Поскольку компилятор Фортран игнорировал пробелы, DO 10 I был принят за имя переменной, а оператор DO 10 I = 1.100 привел к присвоению 1.100 переменной с именем DO10I вместо введения цикла.

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

Почти все современные языки программирования решили не заходить так далеко, игнорируя пробелы

Пробелы как отступы

Есть еще одна важная ситуация, когда в коде Python важны пробелы. Отступ — пробел слева от первого токена в строке — имеет особое значение.

Большинство интерпретируемых языков игнорируют начальные пробелы перед операторами. Например, рассмотрим этот сеанс командной строки Windows:

C:\Users\john>echo foo
foo

C:\Users\john>    echo foo
foo

Примечание. В окне командной строки команда echo отображает свои аргументы в консоли, как и функция print() в Python. Подобное поведение можно наблюдать из окна терминала в macOS или Linux.

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

Теперь попробуйте более или менее то же самое с интерпретатором Python:

>>> print('foo')
foo
>>>     print('foo')

SyntaxError: unexpected indent

Начальный пробел перед вторым оператором print() вызывает исключение SyntaxError.

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

Заключение

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

Оригинальная статья: https://realpython.com/python-program-structure
GitHub: https://github.com/krismile37/scripts-from-article

Подготовка перевода Кристина Гайфулина

Опубликовано Вадим В. Костерин

ст. преп. кафедры ЦЭиИТ. Автор более 130 научных и учебно-методических работ. Лауреат ВДНХ (серебряная медаль).

Оставьте комментарий

Ваш адрес email не будет опубликован.