4. Условные предложения

4.1. Оператор деления по модулю

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

>>> quotient = 7 / 3
>>> print quotient
2
>>> remainder = 7 % 3
>>> print remainder
1

7, деленное на 3, дает 2, да 1 в остатке.

Оказывается, оператор деления по модулю может быть удивительно полезным. Например, можно проверить, делится ли одно число на другое — если x % y дает 0, то x делится на y.

Можно также извлечь из числа самую правую цифру или несколько цифр. Например, x % 10 даст самую правую цифру числа x. Аналогично, x % 100 даст две последние цифры.

4.2. Логические значения и выражения

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

Существуют только два логических значения: True (англ.: истина) и False (англ.: ложь). Логические значения записываются с большой буквы, а true и false не являются логическими значениями.

>>> type(True)
<type 'bool'>
>>> type(true)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'true' is not defined

Логическое выражение — это выражение, вычисление которого дает значение логического типа. Так, оператор == сравнивает два значения и производит логическое значение:

>>> 5 == 5
True
>>> 5 == 6
False

В первом предложении два операнда равны, поэтому выражение дает True. Во втором предложении 5 не равно 6, и потому получаем False.

Оператор == — это один из операторов сравнения; другие перечислены ниже:

x != y               # x не равно y
x > y                # x больше y
x < y                # x меньше y
x >= y               # x больше или равно y
x <= y               # x меньше или равно y

Хотя эти операторы, вероятно, вам знакомы, будьте внимательны при их использовании в Python. Распространенная ошибка — использование одного знака равенства = вместо двух ==. Помните, что = — оператор присваивания, а == — оператор сравнения. А операторов =< и => не существует.

4.3. Логические операторы

Есть три логических оператора: and (англ.: и), or (англ.: или) и not (англ.: не). Семантика этих операторов подобна значениям соответствующих слов в английском языке. Например, выражение x > 0 and x < 10 дает True, если x больше 0 и меньше 10.

Выражение n % 2 == 0 or n % 3 == 0 дает True, если хотя бы одно из условий дает True, то есть, если число делится без остатка на 2 или на 3.

Наконец, оператор not отрицает логическое выражение, так что not (x > y) дает True, если (x > y) дает False, то есть, если x меньше или равно y.

4.4. Условное выполнение

Для того, чтобы писать полезные программы, нужно уметь проверять условия и, в зависимости от результата проверки, поступать тем или иным образом. Условные предложения дают нам такую возможность. Самое простое из них — предложение ``if`` (англ.: если):

if x > 0:
    print "x is positive"

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

Предложение if строится так:

if логическое_выражение:
    предложение
    ...

Как и определение функции из предыдущей главы, и другие составные предложения, предложение if состоит из заголовка и тела. Заголовок начинается с ключевого слова if, за которым следует логическое выражение и двоеточие (:).

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

Тело if выполняется, если вычисление логического выражения дает True. Тело пропускается, если логическое выражение дает False.

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

if True:
    pass

Здесь условие всегда истинно, поэтому тело всегда выполняется. Но ничего не делает.

4.5. Альтернативное выполнение

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

if x % 2 == 0:
    print x, "is even"
else:
    print x, "is odd"

Если остаток от деления x на 2 равен 0, значит, x является четным числом, и программа выводит сообщение об этом. Если же условие оказывается ложным, то выполняется второй блок, следующий за else (англ.: иначе), и выводится сообщение о том, что число нечетное. Поскольку условие может быть либо истинным либо ложным, будет выполнена только одна из двух альтернатив. Альтернативы называют ветками, потому что они разветвляют поток выполнения.

Маленькое отступление. Если вам понадобится часто проверять четность (нечетность) чисел, то вы можете завернуть этот код в функцию:

def print_parity(x):
    if x % 2 == 0:
        print x, "is even"
    else:
        print x, "is odd"

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

>>> print_parity(17)
17 is odd.
>>> y = 41
>>> print_parity(y+1)
42 is even.

4.6. Сцепление условных предложений

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

if x < y:
    print x, "is less than", y
elif x > y:
    print x, "is greater than", y
else:
    print x, "and", y, "are equal"

elif является сокращением от else if (англ.: иначе если). По-прежнему будет выполняться только одна ветка. Предложений elif может быть сколько угодно, но предложение else может быть только одно (или ни одного), в качестве самой последней ветки:

if choice == 'a':
    function_a()
elif choice == 'b':
    function_b()
elif choice == 'c':
    function_c()
else:
    print "Invalid choice."

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

4.7. Вложенные условные предложения

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

if x == y:
    print x, "and", y, "are equal"
else:
    if x < y:
        print x, "is less than", y
    else:
        print x, "is greater than", y

Внешнее условное предложение состоит из двух веток выполнения. Первая ветка содержит предложение print. Вторая ветка содержит предложение if, которое, в свою очередь, включает две ветки. Каждая из них содержит предложение print (хотя они также могли бы содержать вложенные условные предложения).

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

Например, сравните следующие фрагменты кода:

if choice == 'a':
    function_a()
elif choice == 'b':
    function_b()
elif choice == 'c':
    function_c()
else:
    print "Invalid choice."
if choice == 'a':
    function_a()
else:
    if choice == 'b':
        function_b()
    else:
        if choice == 'c':
            function_c()
        else:
            print "Invalid choice."

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

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

if 0 < x:
    if x < 10:
        print "x is a positive single digit."

Предложение print выполняется, только если выполняются условия в двух условных предложениях, поэтому можно воспользоваться оператором and:

if 0 < x and x < 10:
    print "x is a positive single digit."

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

if 0 < x < 10:
    print "x is a positive single digit."

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

4.8. Предложение return

Предложение return (англ.: возвращать(ся)) позволяет завершить выполнение функции раньше, чем достигнут ее конец. Это может понадобиться при обнаружении ошибки:

def print_square_root(x):
    if x <= 0:
        print "Positive numbers only, please."
        return

    result = x**0.5
    print "The square root of", x, "is", result

Функция print_square_root имеет параметр с именем x. Первое, что она делает, это проверяет, не является ли x меньше или равным 0, и, если да, то выводит сообщение об ошибке и завершается с помощью return. Поток выполнения продолжится в вызывающей программе, а остальные предложения функции не будут выполнены.

4.9. Ввод с клавиатуры

В разделе Ввод главы 2 мы встречались со встроенными функциями Python, которые получают ввод с клавиатуры: raw_input и input. Теперь давайте посмотрим на них внимательнее.

Когда вызывается одна из этих функций, программа “останавливается” и ждет, чтобы пользователь что-нибудь ввел с клавиатуры. Когда пользователь нажимает клавишу Ввод, выполнение программы возобновляется и raw_input возвращает то, что ввел пользователь, как значение типа str:

>>> my_input = raw_input()
One two three
>>> print my_input
One two three

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

>>> name = raw_input("What is your name? ")
What is your name? Whinnie the Pooh
>>> print name
Whinnie the Pooh

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

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

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

prompt = "What...is the airspeed velocity of an unladen swallow?\n"
speed = input(prompt)

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

>>> speed = input(prompt)
What...is the airspeed velocity of an unladen swallow?
What do you mean, an African or a European swallow?
...
SyntaxError: invalid syntax

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

>>> speed = input(prompt)
What...is the airspeed velocity of an unladen swallow?
'What do you mean, an African or a European swallow?'
>>> speed
'What do you mean, an African or a European swallow?'
>>>

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

4.10. Преобразование типа

Каждый тип Python имеет соответствующую функцию, которая преобразует значение другого типа в значение данного типа. Например, функция int(аргумент) принимает любое значение и, если возможно, преобразует его в целое, или, в противном случае, сообщает об ошибке:

>>> int("32")
32
>>> int("Hello")
ValueError: invalid literal for int() with base 10: 'Hello'

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

>>> int(-2.3)
-2
>>> int(3.99999)
3
>>> int("42")
42
>>> int(1.0)
1

Функция float(аргумент) преобразует целые числа и строки в числа с плавающей точкой:

>>> float(32)
32.0
>>> float("3.14159")
3.14159
>>> float(1)
1.0

Может показаться странным, что Python различает целочисленное значение 1 и значение с плавающей точкой 1.0. Оба они представляют одно и то же число, но принадлежат разным типам. И потому они по разному представляются внутри компьютера.

Функция str(аргумент) преобразует любой переданный ей аргумент в значение типа str:

>>> str(32)
'32'
>>> str(3.14149)
'3.14149'
>>> str(True)
'True'
>>> str(true)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'true' is not defined

Как уже было сказано, True есть логическое значение, а true логическим значением не является.

Особенно интересная ситуация с преобразованием в логические значения:

>>> bool(1)
True
>>> bool(0)
False
>>> bool("Ni!")
True
>>> bool("")
False
>>> bool(3.14159)
True
>>> bool(0.0)
False

Python ассоциирует логические значения со значениями других типов. Для числовых типов нулевые значения считаются ложными, а ненулевые — истинными. Для строк, пустые строки считаются ложными, а непустые — истинными.

4.11. Глоссарий

блок
Группа предложений, идущих друг за другом и имеющих один и тот же отступ.
ветка
Один из возможных путей потока выполнения, определяемых условным предложением.
вложенность
Одна программная структура внутри другой, например, условное предложение внутри ветки другого условного предложения.
логический оператор
Один из операторов, работающих с логическими выражениями: and, or, not.
логическое выражение
Выражение, которое либо истинно либо ложно.
логическое значение
Существуют ровно два логических значения: True и False. Логические значения получаются в результате вычисления логических выражений. Они имеют тип bool.
оператор деления по модулю
Оператор, обозначаемый знаком процента %, который работает с целыми числами и возвращает остаток от деления первого числа на второе.
оператор сравнения
Один из операторов, сравнивающих два значения: ==, !=, >, <, >=, <=.
преобразование типа
Операция, которая берет значение одного типа, а возвращает соответствующее ему значение другого типа.
приглашение
Подсказка, или сообщение для пользователя, предлагающее пользователю ввести данные.
сцепление условных предложений
Условная конструкция с более чем двумя ветками выполнения. Цепочка условий и соответствующих им блоков в Python записывается с помощью ключевых слов if, elif и else.
тело составного предложения
Блок предложений в составном предложении, следующий за заголовком.
условие
Логическое выражение в условном предложении, которое определяет, какая ветка будет выполняться.
условное предложение
Предложение, которое управляет потоком выполнения, проверяя некоторое условие. Для построения условных предложений Python использует ключевые слова if, elif, else.

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

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

    5 % 2
    9 % 5
    15 % 12
    12 % 15
    6 % 6
    0 % 7
    7 % 0
    

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

  2. if x < y:
        print x, "is less than", y
    elif x > y:
        print x, "is greater than", y
    else:
        print x, "and", y, "are equal"
    

    Заверните этот код в функцию с именем compare(x, y). Вызовите compare три раза: с первым аргументом, меньшим, большим и равным второму.

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

    Следующий скрипт Python печатает таблицу истинности для любого логического выражения с двумя переменными p и q:

    expression = raw_input("Enter a boolean expression in two variables, p and q: ")
    
    print " p      q      %s"  % expression
    length = len( " p      q      %s"  % expression)
    print length*"="
    
    for p in True, False:
        for q in True, False:
            print "%-7s %-7s %-7s" % (p, q, eval(expression))
    

    Как работает этот скрипт, вы узнаете в следующих главах. А пока пользуйтесь им, чтобы исследовать логические выражения. Сохраните эту программу в файле p_and_q.py, запустите ее в командной строке и введите p or q в ответ на приглашение ввести логическое выражение. Вы получите следующее:

     p      q      p or q
    =====================
    True    True    True
    True    False   True
    False   True    True
    False   False   False

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

    def truth_table(expression):
        print " p      q      %s"  % expression
        length = len( " p      q      %s"  % expression)
        print length*"="
    
        for p in True, False:
            for q in True, False:
                print "%-7s %-7s %-7s" % (p, q, eval(expression))
    

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

    >>> from p_and_q import *
    >>> truth_table("p or q")
    p      q      p or q
    =====================
    True    True    True
    True    False   True
    False   True    True
    False   False   False
    >>>
    

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

    1. not(p or q)
    2. p and q
    3. not(p and q)
    4. not(p) or not(q)
    5. not(p) and not(q)

    Какие из выражений логически эквивалентны?

  4. Введите следующие выражения в интерактивном режиме Python:

    True or False
    True and False
    not(False) and True
    True or 7
    False or 7
    True and 0
    False or 8
    "happy" and "sad"
    "happy" or "sad"
    "" and "sad"
    "happy" and ""
    

    Проанализируйте результаты. Какие наблюдения вы сделали относительно использования значений разных типов с логическими операторами? Можете записать эти наблюдения в форме простых правил для выражений с операторами and и or?

  5. if choice == 'a':
        function_a()
    elif choice == 'b':
        function_b()
    elif choice == 'c':
        function_c()
    else:
        print "Invalid choice."
    

    Заверните этот код в функцию dispatch(choice). Затем определите функции function_a, function_b и function_c, такие, чтобы они выводили сообщение о том, что они вызваны. Например:

    def function_a():
        print "function_a was called"
    

    Поместите все четыре функции,``dispatch``, function_a, function_b и function_c, в скрипт с именем ch4prob4.py. В конце скрипта добавьте вызов dispatch('b'). Вы должны получить вывод:

    function_b was called...

    И, наконец, измените скрипт так, чтобы пользователь мог ввести ‘a’, ‘b’ или ‘c’. Протестируйте работу измененного скрипта, импортировав его в интерактивный сеанс Python.

  6. Напишите функцию с именем is_divisible_by_3, которая принимает одно целое число в качестве аргумента и выводит “This number is divisible by three.”, если аргумент без остатка делится на 3, или “This number is not divisible by three.” в противном случае.

    Теперь напишите подобную предыдущей функцию is_divisible_by_5.

  7. Обобщите функции, разработанные в предыдущем упражнении, и напишите функцию is_divisible_by_n(x, n), которая принимает два целых аргумента и сообщает, делится ли первый аргумент на второй. Сохраните эту функцию в файле ch04e06.py, импортируйте в интерактивный сеанс Python, и поработайте с ней. Пример работы с функцией:

    >>> from ch04e06 import *
    >>> is_divisible_by_n(20, 4)
    Yes, 20 is divisible by 4
    >>> is_divisible_by_n(21, 8)
    No, 21 is not divisible by 8
    
  8. Что выведет следующая программа?

    if "Ni!":
        print 'We are the Knights who say, "Ni!"'
    else:
        print "Stop it! No more of this!"
    
    if 0:
        print "And now for something completely different..."
    else:
        print "What's all this, then?"
    

    Объясните результат.