{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Инструкция ветвления и циклы" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В данной лекции рассматривается инструкция ветвления (также называемая условной инструкцией и условным оператором) языка программирования Python, которая обеспечивает выполненение определенной последовательности команд в зависимости от некоторых условий. После нее мы поговорим о циклах - специальных инструкциях, позволяющих многократно выполнять один и тот же набор команд." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Содержание лекции" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* [Инструкция ветвления](#Инструкция-ветвления)\n", "* [Инструкции циклов](#Инструкции-циклов)\n", " * [while](#while)\n", " * [for ... in](#for-...-in)\n", "* [Инструкции управления циклами](#Инструкции-управления-циклами)\n", "* [Вопросы для самоконтроля](#Вопросы-для-самоконтроля)\n", "* [Задание](#Задание)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Инструкция ветвления" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Инструкция ветвления в языке программирования Python имеет следующий вид:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n",
    "if if_condition:\n",
    "    if_code_block\n",
    "elif elif_condition_1:\n",
    "    elif_code_block_1\n",
    "...\n",
    "elif elif_condition_N:\n",
    "    elif_code_block_N\n",
    "else:\n",
    "    else_code_block\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Она должна содержать одно предложение `if` и может содержать ноль или более предложений `elif`, а также необязательное предложение `else`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Каждый из компонентов *сondition* в инструкции ветвления представляет собой некоторое выражение, о котором можно сказать, является оно истинным (принимает значение `True`) или ложным (принимает значение `False`). Это означает, что в качестве *condition* может быть использовано любое выражение, результатом которого является значение с типом `bool` или значение, тип которого может быть неявно преобразован интерпретатором к типу `bool`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Каждый из компонентов *code_block* представляет собой последовательность любых инструкций языка Python, в том числе инструкций ветвления и циклов. Обратите внимание, что все инструкции, входящие в эти блоки кода **должны** иметь одинаковый ненулевой отступ относительно стоящего сверху предложения `if`, `elif` или `else`. Общепринятым является использование четырех пробелов в качестве отступа. Среда разработки Jupyter Notebook упрощает написание кода, самостоятельно расставляя отступы нужного размера там, где требуется." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Принцип работы условной инструкции следующий:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. интерпретатор вычисляет выражение *if_condition*, и если оно истинно, выполняет все инструкции из *if_code_block* и завершает обработку инструкции ветвления\n", "2. если *if_condition* ложно, то интерпретатор по очереди (сверху вниз) вычисляет выражения *elif_condition_k* пока не встретит первое, которое будет истинно, а затем выполняет соответствующий блок кода *elif_code_block_k* и завершает обработку инструкции ветвления\n", "3. если ни одно из условий *elif_condition_k* не было истинным или предложения `elif` отсутствуют, интерпретатор выполняет *else_code_block*, если есть предложение `else`, и завершает обработку инструкции ветвления" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Приведем простейший пример инструкции ветвления:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "this string is printed only if s1 equals s2\n", "this too\n", "this string is always printed\n" ] } ], "source": [ "s1 = 'abc'\n", "s2 = 'abc'\n", "\n", "if s1 == s2:\n", " print('this string is printed only if s1 equals s2')\n", " \n", " print('this too')\n", "print('this string is always printed')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В данном примере мы намеренно не самым красивым образом оформили текст нашей программы, чтобы продемонстрировать важность правильной расстановки отступов при использовании инструкции ветвления. Первые две функции `print` имеют отступ относительно нее, и поэтому являются частью *if_code_block*, а следовательно выполняются только при истинности условия `s1 == s2`. Последняя функция `print` не имеет отступа относительно `if`, поэтому не является частью *if_code_block* и выполняется всегда независимо от истинности условия." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В следующем примере демонстрируется, как используется неявное преобразование типа из `int` в `bool`, которое выполняется интерпретатором потому, что выражение встречается в контексте, где ожидается булевое значение (мы упоминали кратко о таком преобразовании в главе [Операции](./05_Operations.ipynb#Неявные-преобразования-типов), но отложили его рассмотрение):" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a is odd\n" ] } ], "source": [ "a = 13\n", "\n", "# остаток от деления на 2 равен либо 1 (преобразуется в True), если число нечетное, \n", "# или 0 (преобразуется в False), если четное\n", "\n", "if a % 2:\n", " print('a is odd')\n", "else:\n", " print('a is even')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Все возможности инструкции ветвления демонстрируются в следующем примере:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "it's spring\n" ] } ], "source": [ "month_number = 5\n", "\n", "if month_number > 2 and month_number < 6:\n", " print('it\\'s spring') # нужно экранировать символ ' внутри строки\n", "elif month_number > 5 and month_number < 9:\n", " print('it\\'s summer')\n", "elif month_number > 8 and month_number < 12:\n", " print('it\\'s autumn')\n", "else:\n", " print('winter is coming')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Небольшие инструкции ветвления иногда заменяют *условными выражениями*, имеющими следующий синтаксис:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n",
    "expression1 if condition else expression2\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Результатом условного выражения становится результат выражения *expression1*, если *codition* равно `True`, или результат выражения *expression2* в противном случае. Приведем пример условного выражения, возвращающего большее из двух чисел:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "20" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 10\n", "b = 20\n", "max_value = a if a > b else b\n", "max_value" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Инструкции циклов" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Цикл** - это специальная конструкция в языках программирования, позволяющая организовать многократное выполнение одного и того же блока кода. В языке Python существуют две инструкции для организации циклов: `while` и `for ... in`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### while" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Рассмотрим синтаксис инструкции `while`:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n",
    "while condition:\n",
    "    while_code_block\n",
    "else:\n",
    "    else_code_block\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Предложение `else` является необязательным. Компоненты *condition* и *code_block* имеют тот же вид и смысл, что и аналогичные для инструкции ветвления. Если в *code_block* цикла содержится другой цикл, то он называется **вложенным** по отношению к первому." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Рассмотрим принцип работы инструкции `while`:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. интерпретатор вычисляет выражение *condition*, и если оно равно `True`, выполняет все инструкции из блока *while_code_block*, а затем повторяет действия этого пункта с начала\n", "2. если *condition* ложно, интерпретатор проверяет наличие предложения `else`, и если оно есть, выполняет блок *else_code_block*\n", "3. интерпретатор завершает выполнения цикла и переходит к следующей инструкции" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "2\n", "3\n", "4\n", "5\n", "end\n", "next instruction\n" ] } ], "source": [ "a = 0\n", "\n", "while a <= 5:\n", " print(a)\n", " a += 1\n", "else:\n", " print('end')\n", "\n", "print('next instruction')\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "С циклами `while` в Python связана распространенная ошибка: если выражение *condition* никогда не возвращает `False`, то цикл выполняется вечно, следовательно программа \"зависает\":" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = 0\n", "\n", "# в следующем цикле мы забыли увеличить значение a, поэтому оно всегда будет 0, следовательно\n", "# условие a < 10 всегда дает True и цикл выполняется вечно\n", "while a < 10:\n", " print(a)\n", "\n", "# эта строчка никогда не выполнится\n", "print('all printed')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Напомним, что если вы допустили ошибку, в результате которой программа зависает, то можно прервать ее выполнение с помощью команды *Interrupt* в меню *Kernel*." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### for ... in" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Инструкция `for ... in` может использоваться только для специальных итерируемых (iterable) типов данных. **Итерируемым** называется тип, представляющий собой набор из множества элементов, к которым можно обращаться по отдельности в некотором порядке. Мы уже знакомы с одним итерируемым типом - это строковый тип `str`. Много других мы узнаем в лекции, посвященной коллекциям." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Рассмотрим синтаксис инструкции `for ... in`:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n",
    "for expression in iterable:\n",
    "    for_code_block\n",
    "else:\n",
    "    else_code_block\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как и для инструкций `if` и `while`, часть `else` инструкции `for ... in` является необязательной." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Компонент *expression* представляет собой переменную, которой при выполнении цикла в качестве значения поочередно присваивается каждый элемент из итерируемого типа *iterable*. Тип этой переменной будет тем же, что и тип элемента из *iterable*. В случае строкового типа `str`, тип каждого отдельного символа в ней тоже `str`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Опишем принцип работы инструкции `for ... in`:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. пока не все элементы итерируемого типа были обработаны циклом, интерпретатор берет следующий из них и присваивает его переменной, имя которой указано в *expression*, а затем выполняет блок *for_code_block*\n", "2. когда все элементы итерируемого типа были обработаны, интерпретатор проверяет наличие предложения `else`, и если оно есть, выполняет блок *else_code_block*\n", "3. интерпретатор завершает выполнения цикла и переходит к следующей инструкции" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Рассмотрим пример, в котором мы дублируем каждый символ строки:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'00112233445566778899'" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s = '0123456789'\n", "result = '' # это переменная, в которую мы постепенно будем записывать продублированные символы из s\n", "\n", "for symbol in s:\n", " result += symbol * 2 # symbol имеет тип str, используем строковую операцию дублирования\n", "\n", "result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Инструкции управления циклами" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В Python существуют инструкции `break` и `continue`, предназаначенные для изменения последовательности выполнения команд цикла. Обе они могут использоваться только внутри инструкций циклов `while` и `for ... in` (в их *code_block*), в противном случае интерпретатор генерирует исключение `SyntaxError`:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "ename": "SyntaxError", "evalue": "'break' outside loop (, line 2)", "output_type": "error", "traceback": [ "\u001b[1;36m File \u001b[1;32m\"\"\u001b[1;36m, line \u001b[1;32m2\u001b[0m\n\u001b[1;33m break\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m 'break' outside loop\n" ] } ], "source": [ "a = 1\n", "break" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Инструкция `continue` говорит интерпретатору, что вместо того, чтобы выполнить следующие после нее инструкции цикла, он должен перейти в его начало. Инструкция `break` означает, что нужно завершить выполнение цикла и перейти на следующую после него инструкцию в коде. Если программа выходит из цикла по инструкции `break`, то блок кода, написанный в части `else`, не выполняется." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "es sring\n" ] } ], "source": [ "s = 'test string. this part will not be processed'\n", "result = ''\n", "\n", "# копируем в result все символы, кроме буквы 't'\n", "# если встречаем символ '.', то прекращаем обработку строки s\n", "\n", "# обратите внимание, что continue и break является частью блока кода инструкций ветвления,\n", "# потому что нам нужно, чтобы они выполнялись только при истинности соответствующих условий\n", "\n", "for symbol in s:\n", " if symbol == '.':\n", " break # по условию задачи, нам больше не нужно обрабатывать строку s, поэтому\n", " # выходим из цикла\n", " \n", " if symbol == 't':\n", " continue # нам не нужна эта буква, поэтому мы не хотим, чтобы следующая строчка была\n", " # выполнена и даем интерпретатору команду вернуться в начало цикла\n", " \n", " result += symbol\n", "else:\n", " print('whole string processed') # сообщение, что строка s полностью обработана НЕ будет\n", " # выведено, если программа вышла из цикла с помощью break\n", "\n", "print(result) # на эту строчку мы переходим, когда заканчивается выполнение инструкции for" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Инструкция `continue` используется довольно редко, потому что, как правило, можно обойтись без нее, причем программа станет выглядеть лучше и понятнее. Например, в предыдущем примере мы могли бы написать так:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```\n", "if symbol != 't':\n", " result += symbol\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Инструкция `break` напротив используется регулярно, например в случаях, если необходимо прервать выполнение цикла при наступлении некоторой ошибочной ситуации:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n",
    "while main_condition:\n",
    "    do something\n",
    "    \n",
    "    if error_occurred:\n",
    "        break\n",
    "     \n",
    "    do something\n",
    "    \n",
    "    if error_occurred:\n",
    "        break\n",
    "    ...\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В заключение отметим, что инструкции `break` и `continue` оказывают влияние только непосредственно на тот цикл, внутри которого они встречаются. Рассмотрим такой пример:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a = 0\n", "b = 0\n", "a = 1\n", "b = 0\n", "a = 2\n", "b = 0\n" ] } ], "source": [ "a = 0\n", "b = 0\n", "\n", "while a < 3:\n", " print('a =', a)\n", " a += 1\n", " \n", " while b < 3: # инструкция break, которая идет внутри этого цикла, оказывает влияние только на него\n", " print('b =', b)\n", " break\n", " b += 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как только во внутреннем цикле выполняется инструкция `break`, интерпретатор завершает его работу, то есть инструкция `b += 1` не обрабатывается. Мы говорили, что после завершения цикла интерпретатор переходит на следующую после него инструкцию. В случае, когда `break` встречается во вложенном цикле, следующей инструкцией становится либо следующая после вложенного цикла инструкция внешнего цикла, либо, если такой инструкции нет (как в нашем случае), интерпретатор переходит в начало внешнего цикла." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Вопросы для самоконтроля" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. Что такое цикл? Какие инструкции циклов есть в Python?\n", "2. Что такое итерируемый тип данных? Приведите пример.\n", "3. Что такое вложенный цикл?\n", "3. С помощью какой инструкции можно прервать выполнение цикла?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Задание" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. Программа в [примере](#seasons_example) про времена года содержит потенциальную ошибку - найдите опасный участок кода и исправьте его.\n", "2. Перепишите пример с поиском корней квадратного уравнения из задания [предыдущей лекции](./05_Operations.ipynb#Задание) таким образом, чтобы корни вычислялись только в том случае, если дискриминант неотрицателен.\n", "3. Напишите программу, которая создает копию некоторой строки текста на английском языке, в которой отсутствуют все гласные буквы (\"a\", \"e\", \"i\", \"o\", \"u\"). Для решения задачи используйте два цикла `for ... in`, один из которых вложен в другой. Внешний цикл `for ... in` проходит по всем символам превоначальной строки, а внутренний - по специальной строке, содержащей только гласные. В результате выполнения внутреннего цикла можно определить, присутствует ли текущий символ среди гласных символов, и принять на основании этого решение, добавлять его в результат или нет." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "[Предыдущая: Операции](05_Operations.ipynb) |\n", "[Содержание](00_Overview.ipynb#Содержание) |\n", "[Следующая: Функции и модули](07_Functions_And_Modules.ipynb)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4" } }, "nbformat": 4, "nbformat_minor": 2 }