Условные выражения в Python

Оглавление

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

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

Вот тут-то и приходят на помощь управляющие структуры. Управляющая структура определяет порядок выполнения операторов в программе (так называемый поток управления).

Вот что вы узнаете в этом уроке: Вы познакомитесь со своей первой управляющей структурой Python - оператором if.

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

Если погода хорошая, то я подстригу газон. (Подразумевается, что если погода не будет хорошей, то я не буду стричь газон)

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

В общих чертах этот учебник выглядит следующим образом:

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

Готовы? Начинаем!

Вступление к оператору if

Для начала мы рассмотрим самый базовый тип оператора — if. В своей простейшей форме он выглядит так:

if <expr>:
    <statement>

В форме, показанной выше:

  • <expr> — это выражение, оцениваемое в контексте Boolean, как обсуждалось в разделе Логические операторы в учебнике "Операторы и выражения в Python".
  • <statement> — это допустимое утверждение Python, которое должно быть с отступом. (Вы увидите, почему очень скоро.)

Если <expr> истинно (оценивается в значение, которое является "истинным"), то выполняется <statement>. Если <expr> ложно, то <statement> пропускается и не выполняется.

Обратите внимание, что двоеточие (:), следующее за <expr>, является обязательным. Некоторые языки программирования требуют заключать <expr> в круглые скобки, но Python этого не делает.

Вот несколько примеров такого типа if высказываний:

>>> x = 0
>>> y = 5

>>> if x < y:                            # Truthy
...     print('yes')
...
yes
>>> if y < x:                            # Falsy
...     print('yes')
...

>>> if x:                                # Falsy
...     print('yes')
...
>>> if y:                                # Truthy
...     print('yes')
...
yes

>>> if x or y:                           # Truthy
...     print('yes')
...
yes
>>> if x and y:                          # Falsy
...     print('yes')
...

>>> if 'aul' in 'grault':                # Truthy
...     print('yes')
...
yes
>>> if 'quux' in ['foo', 'bar', 'baz']:  # Falsy
...     print('yes')
...

Примечание: Если вы пробуете эти примеры в интерактивном режиме в сессии REPL, вы обнаружите, что, когда вы нажимаете Enter после ввода оператора print('yes'), ничего не происходит.

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

Группировка заявлений: Отступы и блоки

Пока все хорошо.

Но допустим, вы хотите оценить условие и затем выполнить несколько действий, если оно истинно:

Если погода хорошая, то я буду:

  • Стричь газон
  • Кормить сад
  • Вывести собаку на прогулку

(Если погода плохая, то я не буду делать ничего из перечисленного)

Во всех примерах, приведенных выше, за каждым if <expr>: следует только один <statement>. Необходимо каким-то образом сказать: "Если <expr> истинно, выполните все следующие действия"

Обычный подход, используемый большинством языков программирования, заключается в определении синтаксического аппарата, который объединяет несколько утверждений в одно составное утверждение или блок. Синтаксически блок рассматривается как единое целое. Если он является целью оператора if и <expr> истинен, то выполняются все операторы в блоке. Если <expr> ложно, то ни одно из них не выполняется.

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

Python: Все дело в отступах

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

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

Таким образом, составной оператор if в Python выглядит так:

 1 if <expr>:
 2     <statement>
 3     <statement>
 4     ...
 5     <statement>
 6 <following_statement>

Здесь все утверждения на соответствующем уровне отступа (строки со 2 по 5) считаются частью одного блока. Блок выполняется целиком, если <expr> истинно, или пропускается, если <expr> ложно. В любом случае выполнение продолжается с <following_statement> (строка 6) после этого.

Python conditional statement 

Обратите внимание, что нет маркера, обозначающего конец блока. Скорее, конец блока обозначается строкой, отступ которой меньше, чем у строк самого блока.

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

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

 1 if 'foo' in ['bar', 'baz', 'qux']:
 2     print('Expression was true')
 3     print('Executing statement in suite')
 4     print('...')
 5     print('Done.')
 6 print('After conditional')

Запуск foo.py приводит к такому результату:

Командная строка Windows

C:\> python foo.py
After conditional

Четыре утверждения print() в строках со 2 по 5 имеют отступы на одном уровне друг от друга. Они образуют блок, который был бы выполнен, если бы условие было истинным. Но оно ложно, поэтому все утверждения в блоке пропускаются. После того как конец составного оператора if достигнут (независимо от того, выполняются ли операторы блока в строках со 2 по 5 или нет), выполнение переходит к первому оператору с меньшим уровнем отступа: оператору print() в строке 6.

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

Вот более сложный файл сценария под названием blocks.py:

# Does line execute?                        Yes    No
#                                           ---    --
if 'foo' in ['foo', 'bar', 'baz']:        #  x
    print('Outer condition is true')      #  x

    if 10 > 20:                           #  x
        print('Inner condition 1')        #        x

    print('Between inner conditions')     #  x

    if 10 < 20:                           #  x
        print('Inner condition 2')        #  x

    print('End of outer condition')       #  x
print('After outer condition')            #  x

Ниже показан результат, полученный при выполнении этого сценария:

Командная строка Windows

C:\> python blocks.py
Outer condition is true
Between inner conditions
Inner condition 2
End of outer condition
After outer condition

Примечание: Если вам было интересно, правило off-side - это причина необходимости дополнительной новой строки при вводе многострочных утверждений в сессии REPL. Иначе интерпретатор не сможет узнать, что последнее утверждение блока уже введено.

Что делают другие языки?

Возможно, вам интересно, какие есть альтернативы. Как определяются блоки в языках, которые не придерживаются правила офсайда?

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

# (This is Perl, not Python)
if (<expr>) {
    <statement>;
    <statement>;
    ...
    <statement>;
}
<following_statement>;

C/C++, Java и целый ряд других языков используют фигурные скобки именно таким образом.

Perl conditional statement

Сложное выражение if в C/C++, Perl и Java

В других языках, таких как Алгол и Паскаль, для заключения блоков используются ключевые слова begin и end.

"Что лучше?"

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

Плюсы:

  • Использование отступов в Python является чистым, лаконичным и последовательным.
  • В языках программирования, не использующих правило off-side, отступы в коде совершенно не зависят от определения блока и функции кода. Можно написать код с отступами, которые на самом деле не соответствуют тому, как код выполняется, и таким образом создать ошибочное впечатление, когда человек просто смотрит на него. В Python такая ошибка практически невозможна.
  • Использование отступов для определения блоков заставляет вас поддерживать стандарты форматирования кода, которые вы, вероятно, должны использовать в любом случае.

На негативной стороне:

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

Нравится вам это или нет, но если вы программируете на Python, вы застряли с правилом офсайда. Все управляющие структуры в Python используют его, как вы увидите в нескольких последующих уроках.

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

Предложения else и elif

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

Иногда требуется оценить условие и выбрать один путь, если оно истинно, но указать альтернативный путь, если это не так. Для этого используется предложение else:

if <expr>:
    <statement(s)>
else:
    <statement(s)>

Если <expr> равен true, то выполняется первый набор, а второй пропускается. Если <expr> ложно, то первый набор пропускается, а второй выполняется. В любом случае выполнение возобновляется после второго набора. Оба набора определяются отступами, как описано выше.

В данном примере x меньше 50, поэтому первый набор (строки с 4 по 5) выполняется, а второй набор (строки с 7 по 8) пропускается:

 1 >>> x = 20
 2
 3 >>> if x < 50:
 4 ...     print('(first suite)')
 5 ...     print('x is small')
 6 ... else:
 7 ...     print('(second suite)')
 8 ...     print('x is large')
 9 ...
10 (first suite)
11 x is small

Здесь, с другой стороны, x больше, чем 50, поэтому первый набор пропускается, а второй выполняется:

 1 >>> x = 120
 2 >>>
 3 >>> if x < 50:
 4 ...     print('(first suite)')
 5 ...     print('x is small')
 6 ... else:
 7 ...     print('(second suite)')
 8 ...     print('x is large')
 9 ...
10 (second suite)
11 x is large

Также существует синтаксис для ветвления выполнения на основе нескольких альтернатив. Для этого используется одно или несколько предложений elif (сокращение от else if). Python поочередно оценивает каждое <expr> и выполняет набор, соответствующий первому, который оказался истинным. Если ни одно из выражений не является истинным и указано предложение else, то выполняется его набор:

if <expr>:
    <statement(s)>
elif <expr>:
    <statement(s)>
elif <expr>:
    <statement(s)>
    ...
else:
    <statement(s)>

Можно указать произвольное количество пунктов elif. Предложение else является необязательным. Если он присутствует, то может быть только один, и он должен быть указан последним:

>>> name = 'Joe'
>>> if name == 'Fred':
...     print('Hello Fred')
... elif name == 'Xander':
...     print('Hello Xander')
... elif name == 'Joe':
...     print('Hello Joe')
... elif name == 'Arnold':
...     print('Hello Arnold')
... else:
...     print("I don't know who you are!")
...
Hello Joe

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

Примечание: Использование длинных серий if/elif/else может быть несколько неэлегантно, особенно когда действия представляют собой простые утверждения, такие как print(). Во многих случаях можно найти более питонический способ выполнить то же самое.

Вот одна из возможных альтернатив приведенному выше примеру с использованием метода dict.get():

>>> names = {
...     'Fred': 'Hello Fred',
...     'Xander': 'Hello Xander',
...     'Joe': 'Hello Joe',
...     'Arnold': 'Hello Arnold'
... }

>>> print(names.get('Joe', "I don't know who you are!"))
Hello Joe
>>> print(names.get('Rick', "I don't know who you are!"))
I don't know who you are!

Вспомните из учебника по словарям Python, что метод dict.get() ищет в словаре указанный ключ и возвращает связанное с ним значение, если оно найдено, или заданное значение по умолчанию, если нет.

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

>>> var  # Not defined
Traceback (most recent call last):
  File "<pyshell#58>", line 1, in <module>
    var
NameError: name 'var' is not defined

>>> if 'a' in 'bar':
...     print('foo')
... elif 1/0:
...     print("This won't happen")
... elif var:
...     print("This won't either")
...
foo

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

Однострочные if выражения

Обычно принято писать if <expr> на одной строке и <statement> с отступом на следующей строке следующим образом:

if <expr>:
    <statement>

Но допускается писать весь оператор if в одной строке. Следующий пример функционально эквивалентен приведенному выше:

if <expr>: <statement>

На одной строке может быть даже несколько <statement>, разделенных точками с запятой:

if <expr>: <statement_1>; <statement_2>; ...; <statement_n>

Но что это значит? Есть две возможные интерпретации:

  1. Если <expr> истинно, выполните <statement_1>.

    Затем выполните <statement_2> ... <statement_n> безусловно, независимо от того, истинно ли <expr> или нет.

  2. Если <expr> истинно, выполните все пункты <statement_1> ... <statement_n>. В противном случае не выполняйте ни одного из них.

Python принимает последнюю интерпретацию. Точка с запятой, разделяющая <statements>, имеет больший приоритет, чем двоеточие, следующее за <expr> - на компьютерном жаргоне говорят, что точка с запятой связывается более тесно, чем двоеточие. Таким образом, <statements> рассматриваются как набор, и либо все они выполняются, либо ни один:

>>> if 'f' in 'foo': print('1'); print('2'); print('3')
...
1
2
3
>>> if 'z' in 'foo': print('1'); print('2'); print('3')
...

Несколько утверждений могут быть указаны в одной строке в качестве elif или else, а также:

>>> x = 2
>>> if x == 1: print('foo'); print('bar'); print('baz')
... elif x == 2: print('qux'); print('quux')
... else: print('corge'); print('grault')
...
qux
quux

>>> x = 3
>>> if x == 1: print('foo'); print('bar'); print('baz')
... elif x == 2: print('qux'); print('quux')
... else: print('corge'); print('grault')
...
corge
grault

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

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

>>> x = 3
>>> if x == 1:
...     print('foo')
...     print('bar')
...     print('baz')
... elif x == 2:
...     print('qux')
...     print('quux')
... else:
...     print('corge')
...     print('grault')
...
corge
grault

Если оператор if достаточно прост, то его размещение в одной строке может быть разумным. Что-то вроде этого, вероятно, не вызовет ни у кого особых подозрений:

debugging = True  # Set to True to turn debugging on.

    .
    .
    .

if debugging: print('About to call function foo()')
foo()

Условные выражения (тернарный оператор Python)

Python поддерживает одну дополнительную сущность принятия решений, называемую условным выражением. (В разных местах документации по Python его также называют условным оператором или тернарным оператором). Условные выражения были предложены для добавления в язык в PEP 308 и получили зеленый свет от Гвидо в 2005 году.

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

<expr1> if <conditional_expr> else <expr2>

Он отличается от перечисленных выше форм операторов if тем, что не является управляющей структурой, которая направляет поток выполнения программы. Он действует скорее как оператор, определяющий выражение. В приведенном выше примере сначала оценивается <conditional_expr>. Если оно истинно, то выражение оценивается в <expr1>. Если оно ложно, то выражение оценивается как <expr2>.

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

>>> raining = False
>>> print("Let's go to the", 'beach' if not raining else 'library')
Let's go to the beach
>>> raining = True
>>> print("Let's go to the", 'beach' if not raining else 'library')
Let's go to the library

>>> age = 12
>>> s = 'minor' if age < 21 else 'adult'
>>> s
'minor'

>>> 'yes' if ('qux' in ['foo', 'bar', 'baz']) else 'no'
'no'

Примечание: Условное выражение в Python похоже на синтаксис <conditional_expr> ? <expr1> : <expr2>, используемый во многих других языках - C, Perl, Java и других. Фактически, оператор ?: в этих языках обычно называется тернарным оператором, и, вероятно, поэтому условное выражение Python иногда называют тернарным оператором Python.

В PEP 308 можно увидеть, что синтаксис <conditional_expr> ? <expr1> : <expr2> рассматривался для Python, но в итоге был отклонен в пользу синтаксиса, показанного выше.

Чаще всего условное выражение используется для выбора присвоения переменной. Например, предположим, вы хотите найти большее из двух чисел. Конечно, существует встроенная функция max(), которая делает именно это (и многое другое), и вы можете использовать ее. Но предположим, что вы хотите написать свой собственный код с нуля.

Вы можете использовать стандартный оператор if с предложением else:

>>> if a > b:
...     m = a
... else:
...     m = b
...

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

>>> m = a if a > b else b

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

В следующем примере оператор + связывается сильнее, чем условное выражение, поэтому сначала оцениваются 1 + x и y + 2, а затем условное выражение. Скобки во втором случае не нужны и не меняют результат:

>>> x = y = 40

>>> z = 1 + x if x > y else y + 2
>>> z
42

>>> z = (1 + x) if x > y else (y + 2)
>>> z
42

Если вы хотите, чтобы условное выражение было оценено первым, вам нужно окружить его группирующими скобками. В следующем примере сначала оценивается (x if x > y else y). Результатом является y, который равен 40, поэтому z присваивается 1 + 40 + 2 = 43:

>>> x = y = 40

>>> z = 1 + (x if x > y else y) + 2
>>> z
43

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

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

В выражении <expr1> if <conditional_expr> else <expr2>:

  • Если <conditional_expr> равно true, то возвращается <expr1> и <expr2> не оценивается.
  • Если <conditional_expr> равно false, то возвращается <expr2> и <expr1> не оценивается.

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

>>> 'foo' if True else 1/0
'foo'
>>> 1/0 if False else 'bar'
'bar'

В обоих случаях члены 1/0 не оцениваются, поэтому исключение не возникает.

Условные выражения также можно объединять в цепочки, как альтернативную структуру if/elif/else, как показано здесь:

>>> s = ('foo' if (x == 1) else
...      'bar' if (x == 2) else
...      'baz' if (x == 3) else
...      'qux' if (x == 4) else
...      'quux'
... )
>>> s
'baz'

Не ясно, имеет ли это какое-либо существенное преимущество перед соответствующим оператором if/elif/else, но синтаксически это правильный Python.

Оператор pass

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

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

# This is not Python
if (x)
{
}

Здесь пустые фигурные скобки определяют пустой блок. Perl или C оценит выражение x, а затем, даже если оно окажется истинным, ничего не сделает.

Поскольку Python использует отступы вместо разделителей, невозможно указать пустой блок. Если вы вводите оператор if с помощью if <expr>:, то после него должно следовать что-то либо в той же строке, либо с отступом на следующей строке.

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

if True:

print('foo')

Если вы попытаетесь запустить foo.py, то получите следующее:

Командная строка Windows

C:\> python foo.py
  File "foo.py", line 3
    print('foo')
        ^
IndentationError: expected an indented block

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

if True:
    pass

print('foo')

Теперь foo.py работает без ошибок:

Командная строка Windows

C:\> python foo.py
foo

Заключение

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

  • Вы познакомились с понятием управляющих структур. Это составные операторы, которые изменяют программный поток управления - порядок выполнения операторов программы.
  • Вы узнали, как сгруппировать отдельные операторы в блок или блок.
  • Вы встретили свою первую управляющую структуру, оператор if, который позволяет условно выполнить оператор или блок на основе оценки данных программы.

Все эти понятия имеют решающее значение для разработки более сложного кода на Python.

В следующих двух уроках будут представлены две новые управляющие структуры: оператор while и оператор for. Эти структуры облегчают итерацию, многократное выполнение оператора или блока операторов.

Вернуться на верх