{ "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 (\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
}