Я учусь программировать на Python: циклы, условия и исключения
В третьем выпуске блога о программировании я затрону несколько ключевых понятий, которыми оперирует каждый программист.
Предыдущий выпуск
Для начала кое-что поясню. Блог называется «Я учусь программировать», а не «Я учу программировать» — иначе говоря, это не учебник по программированию. Я не эксперт и не обладаю навыками преподавателя, а потому если и могу взять на себя право кого-то учить, то только себя.
У блога есть две цели. Первая — личная: я хочу систематизировать и укрепить знания, которые у меня есть. Как известно, один из лучших способов учиться — рассказывать и объяснять другим людям то, что знаешь. Именно поэтому я с удовольствием буду читать отзывы и указания на ошибки (в прошлый раз, кстати, ценные замечания были в нашем фейсбуке). Вторая цель — пропагандистская. Я искренне считаю, что сейчас научиться программировать может любой человек, и хочу своим примером показать: раз редактор сайта Look At Me может писать код, то его может писать вообще кто угодно.
Примечание: Весь код приводится для Python версии 3.0 и старше, а потому может не работать на более старых версиях.
В прошлый раз мы написали простую программу, которая вычисляет квадрат введённого целого числа. В конце я назвал два недостатка программы. Во-первых, она перестаёт работать сразу после того, как выводит результат. Во-вторых, она никак не защищена от неточностей при вводе: если мы введём не число, а букву, то программа покажет ошибку и закроется. Начнём с первого недостатка. Чтобы устранить его, введём понятие цикла — конструкции, которая позволяет выполнять один и тот же набор операций сколько угодно раз — при желании даже бесконечно.
def square(int):
return int * int
while True:
number = int(input("Enter an integer: "))
print("The square of {} is {}.".format(number, square(number)))
По сравнению с кодом из предыдущего выпуска блога я внёс лишь одно изменение: поместил две последние строчки в безусловный цикл while True:. Инструкции, выполняемые циклом, помещаются в блок кода с отступом в четыре пробела (по стандарту PEP 8) — прямо как содержимое функции. Циклы с ключевым словом while работают следующим образом:
Интерпретатор видит ключевое слово while и условие, которое задаётся после него. В нашем случае это условие True типа данных boolean. К типу boolean также относится значение False. Иначе говоря, это логический тип, у которого могут быть только два значения — истина или ложь. Слова while и True образуют нечто вроде идиоматической конструкции, говорящей интерпретатору: выполняй инструкции внутри блока бесконечно — если их только вдруг что-то не прервёт.
Интерпретатор выполняет инструкции внутри блока по порядку, от первой строки до последней.
Интерпретатор видит, что блок кода закончился, и проверяет, выполняется ли условие, указанное после ключевого слова while. Если оно выполняется, то интерпретатор возвращается к первой строке блока (обратите внимание — блока, а не всей программы) и идёт по порядку. Если условие не выполняется, то цикл завершается, и интерпретатор переходит к строке, которая следует сразу за последней строкой блока. В нашем случае условие будет выполняться всегда, вне зависимости от того, что происходит внутри блока. Но в теории мы могли бы задать другое условие, например, сделать так, чтобы конструкция сработала только три раза:
i = 0
while i < 3:
number = int(input("Enter an integer: "))
print("The square of {} is {}.".format(number, square(number)))
i++
Я ввёл переменную i и присвоил ей значение 0, а потому она будет типа данных int. Далее я поместил в блок цикла новую строку i++. ++ — это оператор инкремента (увеличения): после его исполнения к значению переменной i прибавляется 1. Запись i++ равноценна записи i = i + 1, просто это более короткий вариант. Таким образом, после третьего исполнения всех инструкций блока значение i достигнет 3, и условие i < 3 (i меньше трёх) перестанет выполняться, и цикл завершится.
Внимание! Только что я привёл не очень грамотный код, а если точнее — не очень питонический код. Хотя интерпретатор его примет, конструкция, которую я использовал, скорее характерна для других языков — например, C (хотя, на мой взгляд, всё равно не очень элегантна). В Python для того, чтобы задать, сколько раз цикл должен выполнить инструкции внутри блока, обычно используют цикл со счётчиком c ключевыми словами for и in, но мы поговорим о нём позже.
Возможно, у кого-то появится вопрос — почему я не поместил строку i = 0 внутрь блока? Не было бы это разумнее, учитывая, что за пределами блока переменная никак не используется? Помимо того, что это просто не очень аккуратный код, помните и то, что если бы мы сделали i = 0 первой строчкой внутри блока, то в начале каждого цикла значение i обнулялось бы. Поясню, чем ошибочен код ниже:
while i < 3:
i = 0
number = int(input("Enter an integer: "))
print("The square of {} is {}.".format(number, square(number)))
i++
В начале первого цикла интерпретатор выполняет инструкцию на первой строке блока и присваивает переменной i значение 0.
Интерпретатор доходит до четвёртой строки блока, проводит операцию увеличения i++, и значение i становится 1.
Интерпретатор, увидев, что блок закончился и условие i < 3 по-прежнему выполняется (1 действительно меньше 3), начинает второй цикл.
Интерпретатор снова выполняет инструкцию на первой строке и присваивает переменной i с текущим значением 1 новое старое значение 0.
Пункты 1–4 повторяются бесконечно. По сути, мы получаем криво написанный аналог цикла while True — значение i никогда не достигнет не то что 3, а даже 2.
Хотя мы исправили первый недостаток, на его месте появился другой. Сейчас наша программа работает бесконечно — из неё невозможно выйти. Но что если задать условие, при котором пользователь, набрав букву q, смог бы выйти из программы? Давайте так и сделаем, а заодно разберём изменения в коде:
def square(int):
return int * int
print("Input 'q' to quit.")
while True:
number = input("Enter an integer: ")
if number == 'q':
break
else:
number = int(number)
print("The square of {} is {}.".format(number, square(number)))
print("Thank you for using the app.")
Обратите внимание на третью и последнюю строки. Я добавил функции print() для вывода сообщений, которые помогут ориентироваться пользователю в программе. Первое сообщение будет всегда выводиться один раз в самом начале, второе — один раз, когда программа заканчивает работу. Внутри блока, относящегося к циклу while True:, появились операторы ветвления if и else. Они позволяют задавать условия выполнения блоков кода и эквивалентны синтаксической конструкции «если ..., то ...». Вот как они работают:
После ключевого слова if задаётся условие. Если это условие выполняется, то интерпретатор переходит к коду внутри блока, следующего за двоеточием. В нашем случае условие — это number == 'q', а вся строка будет читаться как «если значение переменной number — это 'q'». Оператор == значит «равно» — не путайте его с оператором =, который обозначает присваивание (условие number = 'q' выдаст ошибку — это попытка присвоить переменной number, значение которой только что было введено, значение 'q'; нам же нужно сравнить введённое значение со значением 'q').
Внутри блока используется ключевое слово break, говорящее интерпретатору, что ему нужно прервать текущий цикл и перейти к следующей за блоком строкой программы — в нашем случае это последняя строка программы с функцией print(). Блок if ... : находится внутри блока while True:, а потому строка с ключевым словом break записана с отступом в восемь пробелов. Если бы слова break не было, то интерпретатор, миновав блок else:, продолжил бы двигаться дальше по циклу.
Если условие не выполняется, то интерпретатор переходит к следующей за блоком строке, в нашем случае — строке с ключевым словом else. Это слово сообщает интерпретатору, что если условие в if не выполняется, то следует выполнить код внутри блока else:. В этот блок я поместил две операции, которые раньше находились внутри блока while True:.
Таким образом, в программе появляется условие: если введённый нами символ — это q, то работу цикла нужно завершить, если нет, то продолжить, перевести переменную number из типа string в тип int и вывести сообщение с квадратом числа.
Наверняка у многих появится вопрос — а зачем я вынес операцию по переводу переменной number из типа string в тип int на отдельную строку внутри блока else:? Дело в том, что если оставить эту операцию на первой строке цикла, то после ввода символа q программа выдаст ошибку, потому что символ q нельзя перевести в int — то есть сделать целым числом. Иначе говоря, программа выдаст ошибку ещё до того, как успеет проверить условие. Если же мы уберём перенос внутрь блока else, то ввод q позволит выйти из программы. Осталось только сделать так, чтобы ввод других букв не приводил к ошибке.
Чтобы программа не выбрасывала пользователя каждый раз, когда он вводит нецелое число и любую букву, кроме q, мы воспользуемся операторами обработки исключений try и except — и опишем, как они работают:
def square(int):
return int * int
print("Input 'q' to quit.")
while True:
number = input("Enter an integer: ")
if number == 'q':
break
else:
try:
number = int(number)
print("The square of {} is {}.".format(number, square(number)))
except ValueError:
print("Error: incorrect value. Please try again.")
print("Thank you for using the app.")
Конструкция с ключевыми словами try и except стала частью блока else: из цикла while True:, а потому оба слова введены с отступом в восемь пробелов, а строки внутри блоков try: и except ValueError: — в 12 пробелов.
Слово try указывает интерпретатору, что нужно проверить, выполнимы ли инструкции внутри блока, следующего за двоеточием. Если они выполнимы и не выдают ошибок, то нужно их выполнить и вернуться в предыдущий блок кода (в нашем случае блок while True:), если невыполнимы — перейти в блок с ключевым словом except.
Блок слова except указывает, что делать в том случае, если операции из блока try: оказались невыполнимы. В нашем случае мы ожидаем ошибку типа ValueError и указываем её после ключевого слова — хотя можно было и просто написать except:, то есть что делать в случае, если появится ошибка любого типа.
Таким образом программа проверяет, что ввёл пользователь. Если он ввёл нецелое число и любую букву, кроме q, то программа, поняв, что невозможно выполнить первую строку из блока try:, выводит сообщение об ошибке из блока except: и продолжает цикл дальше. Теперь, если вы введёте неверный символ, программа сразу предложит вам попробовать это сделать ещё раз, а не прекратит работу.
Теперь программа делает всё так, как я хотел с самого начала. Её можно расширить — например, чтобы она высчитывала не только квадрат, но и куб введённого числа или работала с десятичными дробями, для чего надо использовать тип данных float. Я этого делать в блоге не буду, но если вдруг есть желание — вперёд.
Возможно, я сегодня переборщил с новым материалом, и это всё будет непросто переварить. Но я намеренно не углублялся в каждую из тем, а лишь затронул их: мы ещё не раз будем встречать эти понятия и наловчимся, как ими пользоваться. Пока я не решил, что будет в следующем выпуске — думаю, либо простенькая текстовая игра, либо задание, которое я дал себе в первом выпуске блога. Главное, что в следующий раз мы укрепим и расширим представления, полученные в этом и предыдущем выпуске.
Пожалуйста, если вам что-то непонятно, пишите комментарии под постом и в социальных сетях — буду рад и любым другим отзывам. Если вы более опытный в программировании человек, чем я, то с удовольствием выслушаю содержательную критику. Спасибо, и до следующего раза!
Читайте также:
Полные курсы Python на Codeacademy и Treehouse*
* — платные курсы, но есть пробный период
Развитие программистского мышления на Udacity
Циклы в Python на Learnpython.org и Python Course
Условия в Python на Learnpython.org и Python Course
Исключения в Python на Learnpython.org и Python Course
изображение via Shutterstock
Комментарии
Подписаться