diff --git a/control.ipynb b/control.ipynb
new file mode 100644
index 0000000..8af4885
--- /dev/null
+++ b/control.ipynb
@@ -0,0 +1,1507 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "# Управляющие структуры и функции\n",
+ "\n",
+ " \n",
+ "**С.В. Лемешевский** (email: `sergey.lemeshevsky@gmail.com`), Институт математики НАН Беларуси\n",
+ "\n",
+ "Date: **Mar 18, 2020**\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "Рассматриваться управляющие структуры языка Python: условные\n",
+ "инструкции и циклы, инструкции обработки исключительных ситуаций.\n",
+ "Кроме того рассматривается создание собственных функций, и здесь будет\n",
+ "подробно рассматриваться чрезвычайно гибкий механизм работы с\n",
+ "аргументами функций."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Управляющие структуры\n",
+ "
\n",
+ "\n",
+ "В языке Python условное ветвление реализуется с помощью инструкции\n",
+ "`if`, а циклическая обработка – с помощью инструкций `while` и `for\n",
+ "... in`. В языке Python имеется также такая конструкция, как условное\n",
+ "выражение – вариант инструкции `if`, аналог трехместного оператора\n",
+ "(`?:`), имеющегося в C-подобных языках."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Условное ветвление\n",
+ "\n",
+ "\n",
+ "Общий синтаксис инструкции условного ветвления в языке Python имеет\n",
+ "следующий вид:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "if boolean_expression1:\n",
+ " suite1\n",
+ "elif boolean_expression2:\n",
+ " suite2\n",
+ "...\n",
+ "elif boolean_expressionN:\n",
+ " suiteN\n",
+ "else:\n",
+ " else_suite"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Инструкция может содержать ноль или более предложений\n",
+ "`elif`. Заключительное предложение `else` также является\n",
+ "необязательным. Если необходимо предусмотреть ветку для какого-то\n",
+ "особого случая, который не требует никакой обработки, в качестве блока\n",
+ "кода этой ветки можно использовать инструкцию `pass` (она ничего не\n",
+ "делает и просто является инструкцией-заполнителем, используемой там,\n",
+ "где должна находиться хотя бы одна инструкция).\n",
+ "\n",
+ "В некоторых случаях можно сократить инструкцию `if ... else` до\n",
+ "единственного условного выражения. Ниже приводится синтаксис условных\n",
+ "выражений:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "expression1 if boolean_expression else expression2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Если логическое выражение `boolean_expression` возвращает значение\n",
+ "`True`, результатом всего условного выражения будет результат\n",
+ "выражения `expression1`, в противном случае – результат выражения\n",
+ "`expression2`.\n",
+ "\n",
+ "\n",
+ "> **Об использовании скобок.**\n",
+ ">\n",
+ "> Предположим, что нам необходимо записать в переменную `width` значение\n",
+ "> 100 и прибавить к нему 10, если переменная margin имеет значение\n",
+ "> `True`. Мы могли бы написать такое выражение:\n",
+ ">"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "width = 100 + 10 if margin else 0\t# ОШИБКА!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "> \n",
+ "> Особенно неприятно, что эта строка программного кода работает\n",
+ "> правильно, когда переменная `margin` имеет значение `True`, записывая\n",
+ "> значение 110 в переменную width. Но когда переменная `margin` имеет\n",
+ "> значение `False`, в переменную `width` вместо 100 будет записано\n",
+ "> значение 0. Это происходит потому, что интерпретатор Python\n",
+ "> воспринимает выражение 100 + 10 как часть `expression1` условного\n",
+ "> выражения. Решить эту проблему можно с помощью круглых скобок:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "width = 100 + (10 if margin else 0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "> \n",
+ "> Кроме того, круглые скобки делают программный код более понятным\n",
+ "> для человека.\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "Условные выражения могут использоваться для видоизменения сообщений,\n",
+ "выводимых для пользователя. Например, при выводе числа обработанных\n",
+ "файлов, вместо того чтобы печатать «0 файл(ов)», «1 файл(ов)» или\n",
+ "что-то подобное, можно было бы использовать пару условных выражений:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [
+ {
+ "ename": "NameError",
+ "evalue": "name 'count' is not defined",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"{0} файл{1}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcount\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcount\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0;36m0\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m\"нет\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m\"ов\"\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcount\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;36m10\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m\"a\"\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcount\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;36m10\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m\"\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;31mNameError\u001b[0m: name 'count' is not defined"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"{0} файл{1}\".format((count if count != 0 else \"нет\"), (\"ов\" if count % 10 not in [1, 2, 3, 4] else (\"a\" if count % 10 != 1 else \"\"))))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Циклы\n",
+ "\n",
+ "\n",
+ "В языке Python есть две инструкции циклов – `while` и `for ... in`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Циклы `while`\n",
+ "\n",
+ "\n",
+ "\n",
+ "Ниже приводится полный синтаксис цикла `while`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "while boolean_expression:\n",
+ " while_suite\n",
+ "else:\n",
+ " else_suite"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Предложение `else` является необязательным. До тех пор, пока выражение\n",
+ "`boolean_expression` возвращает значение `True`, в цикле будет\n",
+ "выполняться блок `while_suite`. Если выражение `boolean_expression`\n",
+ "вернет значение `False`, цикл завершится, и при наличии предложения `else`\n",
+ "будет выполнен блок `else_suite`. Если внутри блока `while_suite`\n",
+ "выполняется инструкция `continue`, то управление немедленно передается\n",
+ "в начало цикла и выражение `boolean_expression` вычисляется снова. \n",
+ "Если цикл не завершается нормально, блок предложения `else` не\n",
+ "выполняется.\n",
+ "\n",
+ "Необязательное предложение `else` имеет несколько сбивающее с толку\n",
+ "название, поскольку оно выполняется во всех в случаях, когда цикл\n",
+ "нормально завершается. Если цикл завершается в результате выполнения\n",
+ "инструкции `break` или `return`, когда цикл находится внутри функции\n",
+ "или метода, или в результате исключения, то блок `else_suite` \n",
+ "предложения `else` не выполняется. (При возникновении исключительной\n",
+ "ситуации интерпретатор Python пропускает предложение `else` и пытается\n",
+ "отыскать подходящий обработчик исключения, о чем будет рассказываться\n",
+ "в следующем разделе.) Плюсом такой реализации является одинаковое\n",
+ "поведение предложения `else` в циклах `while`, в циклах `for ... in` и\n",
+ "в блоках `try ... except`. \n",
+ "\n",
+ "Рассмотрим пример, демонстрирующий предложение `else` в действии.\n",
+ "Методы `str.index()` и `list.index()` возвращают индекс заданной\n",
+ "подстроки или элемента или возбуждают исключение `ValueError`, если\n",
+ "подстрока или элемент не найдены. Метод `str.find()` делает то же\n",
+ "самое, но в случае неудачи он не возбуждает исключение, а возвращает\n",
+ "значение `-1`. Для списков не существует эквивалентного метода, но \n",
+ "при желании мы могли бы создать такую функцию, использующую\n",
+ "цикл `while`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def list_find(lst, target):\n",
+ " index = 0\n",
+ " while index < len(lst):\n",
+ " if lst[index] == target:\n",
+ " break\n",
+ " index += 1\n",
+ " else:\n",
+ " index = -1\n",
+ " return index"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "Эта функция просматривает список в поисках заданного элемента\n",
+ "`target`. Если искомый элемент будет найден, инструкция `break`\n",
+ "завершит цикл и вызывающей программе будет возвращен соответствующий\n",
+ "индекс. Если искомый элемент не будет найден, цикл достигнет конца \n",
+ "списка и завершится обычным способом. В случае нормального завер-\n",
+ "шения цикла будет выполнен блок в предложении `else`, индекс получит\n",
+ "значение `-1` и будет возвращен вызывающей программе."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Циклы `for`\n",
+ "\n",
+ "\n",
+ "Подобно циклу `while`, полный синтаксис цикла `for ... in` также\n",
+ "включает необязательное предложение `else`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "for expression in iterable:\n",
+ " for_suite\n",
+ "else:\n",
+ " else_suite"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "В качестве выражения `expression` обычно используется либо\n",
+ "единственная переменная, либо последовательность переменных, как\n",
+ "правило, в форме кортежа. Если в качестве выражения `expression`\n",
+ "используется кортеж или список, каждый элемент итерируемого объекта\n",
+ "`iterable` распаковывается в элементы `expression`.\n",
+ "\n",
+ "Если внутри блока `for_suite` встретится инструкция `continue`,\n",
+ "управление будет немедленно передано в начало цикла и будет начата\n",
+ "новая итерация. Если цикл завершается по выполнении всех итераций и в\n",
+ "цикле присутствует предложение `else`, выполняется блок\n",
+ "`else_suite`. Если выполнение цикла прерывается принудительно\n",
+ "(инструкцией `break` или `return`), управление немедленно передается\n",
+ "первой инструкции, следующей за циклом, а дополнительное предложение\n",
+ "`else` при этом пропускается. Точно так же, когда возбуждается\n",
+ "исключение, интерпретатор Python пропускает предложение `else` и\n",
+ "пытается отыскать подходящий обработчик исключения (о чем будет\n",
+ "рассказываться в следующем разделе).\n",
+ "\n",
+ "Ниже приводится версия функции `list_find()`, реализованная на базе\n",
+ "цикла `for ... in`, которая так же, как и версия на базе цикла\n",
+ "`while`, демонстрирует предложение `else` в действии:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [
+ {
+ "ename": "IndentationError",
+ "evalue": "expected an indented block (, line 4)",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m4\u001b[0m\n\u001b[0;31m break\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m expected an indented block\n"
+ ]
+ }
+ ],
+ "source": [
+ "def list_find(lst, target):\n",
+ " for index, x in enumerate(lst):\n",
+ " if x == target:\n",
+ " break\n",
+ " else:\n",
+ " index = -1\n",
+ " return index"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Как видно из этого фрагмента, переменные, созданные в выражении\n",
+ "`expression` цикла `for ... in`, продолжают существовать после\n",
+ "завершения цикла. Как и любые локальные переменные, они прекращают\n",
+ "свое существование после выхода из области видимости, включающей их.\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Обработка исключений\n",
+ "\n",
+ "\n",
+ "Об ошибках и исключительных ситуациях интерпретатор Python сообщает\n",
+ "посредством возбуждения исключений, хотя в некоторых библиотеках\n",
+ "сторонних разработчиков еще используются устаревшие приемы, такие как\n",
+ "возврат «ошибочного» значения."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Перехват и возбуждение исключений\n",
+ "\n",
+ "\n",
+ "Перехватывать исключения можно с помощью блоков `try ... except`,\n",
+ "которые имеют следующий синтаксис:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " try_suite\n",
+ "except exception_group1 as variable1:\n",
+ " except_suite1\n",
+ "...\n",
+ "except exception_groupN as variableN:\n",
+ " except_suiteN\n",
+ "else:\n",
+ " else_suite\n",
+ "finally:\n",
+ " finally_suite"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Эта конструкция должна содержать хотя бы один блок except, а блоки\n",
+ "`else` и `finally` являются необязательными. Блок `else_suite`\n",
+ "выполняется, только если блок `try_suite` завершается обычным\n",
+ "способом, и не выполняется в случае возникновения исключения. Если\n",
+ "блок `finally` присутствует, он выполняется всегда и в последнюю\n",
+ "очередь. \n",
+ "\n",
+ "Каждая группа `exception_group` в предложении except может быть\n",
+ "единственным исключением или кортежем исключений в круглых\n",
+ "скобках. Часть `as variable` в каждой группе является\n",
+ "необязательной. В случае ее использования в переменную variable\n",
+ "записывается ссылка на исключение, которое возникло, благодаря этому к\n",
+ "нему можно будет обратиться в блоке `except_suite`.\n",
+ "\n",
+ "Если исключение возникнет во время выполнения блока `try_suite`,\n",
+ "интерпретатор поочередно проверит каждое предложение `except`. Если \n",
+ "будет найдена соответствующая группа `exception_group`, будет выполнен\n",
+ "соответствующий блок `except_suite`. Соответствующей считается группа,\n",
+ "в которой присутствует исключение того же типа, что и возникшее\n",
+ "исключение, или возникшее исключение является подклассом одного из\n",
+ "исключений, перечисленных в группе.\n",
+ "\n",
+ "На рис. [control:except:fig:1](#control:except:fig:1) демонстрируется порядок выполнения\n",
+ "типичной конструкции `try ... except ... finally`. \n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "Ниже приводится окончательная версия функции `list_find()`, на этот\n",
+ "раз она использует механизм обработки исключения:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def list_find(lst, target):\n",
+ " try:\n",
+ " index = lst.index(target)\n",
+ " except ValueError:\n",
+ " index = -1\n",
+ " return index"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Здесь мы использовали конструкцию `try ... except` для преобразования\n",
+ "исключения в возвращаемое значение. Аналогичный подход можно\n",
+ "использовать для перехвата одних исключений и возбуждения других – с\n",
+ "этим приемом мы познакомимся очень скоро. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Возбуждение исключений\n",
+ "\n",
+ "\n",
+ "Исключения представляют собой удобное средство управления потоком\n",
+ "выполнения. Мы можем воспользоваться этим, используя либо встроенные\n",
+ "исключения, либо создавая свои собственные и возбуждая нужные нам,\n",
+ "когда это необходимо. Возбудить исключение можно одним из двух\n",
+ "способов:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [
+ {
+ "ename": "NameError",
+ "evalue": "name 'exception' is not defined",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mexception\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;32mraise\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mNameError\u001b[0m: name 'exception' is not defined"
+ ]
+ }
+ ],
+ "source": [
+ "raise exception(args)\n",
+ "raise"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "В первом случае, то есть когда явно указывается возбуждаемое\n",
+ "исключение, оно должно быть либо встроенным, либо нашим собственным,\n",
+ "наследующим класс `Exception`. Если исключению в виде аргумента\n",
+ "передается некоторый текст, этот текст будет выведен на экран, если\n",
+ "исключение не будет обработано программой. Во втором случае, то есть \n",
+ "когда исключение не указывается, инструкция `raise` повторно возбудит\n",
+ "текущее активное исключение, а в случае отсутствия активного\n",
+ "исключения будет возбуждено исключение `TypeError`. \n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Собственные функции\n",
+ "\n",
+ "\n",
+ "Функции представляют собой средство, дающее возможность упаковывать и\n",
+ "параметризовать функциональность. В языке Python можно создать четыре\n",
+ "типа функций: глобальные функции, локальные функции, лямбда-функции и\n",
+ "методы. \n",
+ "\n",
+ "Все функции, которые мы создавали до сих пор, являются *глобальными*\n",
+ "функциями. Глобальные объекты (включая функции) доступны из любой\n",
+ "точки программного кода в том же модуле (то есть в том же самом файле\n",
+ "`.py`), которому принадлежит объект. Глобальные объекты доступны\n",
+ "также и из других модулей, как будет показано в следующей главе.\n",
+ "\n",
+ "*Локальные* функции (их еще называют вложенными функциями) –\n",
+ "это функции, которые объявляются внутри других функций. Эти\n",
+ "функции видимы только внутри тех функций, где они были объявлены – они\n",
+ "особенно удобны для создания небольших вспомогательных функций,\n",
+ "которые нигде больше не используются. \n",
+ "\n",
+ "*Лямбда*-функции – это выражения, поэтому они могут создаваться\n",
+ "непосредственно в месте их использования; они имеют множество\n",
+ "ограничений по сравнению с обычными функциями. Методы – это те же\n",
+ "функции, которые ассоциированы с определенным типом данных и могут\n",
+ "использоваться только в связке с этим типом данных.\n",
+ "\n",
+ "Синтаксис создания функции (глобальной или локальной) имеет следующий\n",
+ "вид:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def functionName(parameters):\n",
+ " suite"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Параметры `parameters` являются необязательными и при наличии более\n",
+ "одного параметра записываются как последовательность идентификаторов\n",
+ "через запятую или в виде последовательности пар `identifier=value`, о\n",
+ "чем вскоре будет говориться подробнее. Например, ниже приводится\n",
+ "функция, которая вычисляет площадь треугольника по формуле Герона:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def heron(a, b, c):\n",
+ " s = (a + b + c) / 2\n",
+ " return math.sqrt(s * (s - a) * (s - b) * (s - c))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Внутри функции каждый параметр, `a`, `b` и `c`, инициализируется\n",
+ "соответствующими значениями, переданными в виде аргументов. При вызове\n",
+ "функции мы должны указать все аргументы, например, `heron(3, 4, 5)`.\n",
+ "Если передать слишком мало или слишком много аргументов, будет\n",
+ "возбуждено исключение `TypeError`. Производя такой вызов, мы \n",
+ "говорим, что используем позиционные аргументы, потому что каждый\n",
+ "переданный аргумент становится значением параметра в соответствующей\n",
+ "позиции. То есть в данном случае при вызове функции параметр `a`\n",
+ "получит значение `3`, параметр `b` – значение `4` и параметр `с` –\n",
+ "значение `5`.\n",
+ "\n",
+ "Все функции в языке Python возвращают какое-либо значение, хотя\n",
+ "вполне возможно (и часто так и делается) просто игнорировать это\n",
+ "значение. Возвращаемое значение может быть единственным значением или\n",
+ "кортежем значений, а сами значения могут быть коллекциями, \n",
+ "поэтому практически не существует никаких ограничений на то, что\n",
+ "могут возвращать функции. Мы можем покинуть функцию в любой\n",
+ "момент, используя инструкцию `return`. Если инструкция `return`\n",
+ "используется без аргументов или если мы вообще не используем\n",
+ "инструкцию `return`, функция будет возвращать значение `None`.\n",
+ "\n",
+ "Некоторые функции имеют параметры, для которых может существо-\n",
+ "вать вполне разумное значение по умолчанию. Например, ниже при-\n",
+ "водится функция, которая подсчитывает количество алфавитных сим-\n",
+ "волов в строке; по умолчанию подразумеваются алфавитные символы\n",
+ "из набора ASCII:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [
+ {
+ "ename": "NameError",
+ "evalue": "name 'string' is not defined",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mdef\u001b[0m \u001b[0mletter_count\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtext\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mletters\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mstring\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mascii_letters\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mletters\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfrozenset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mletters\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mcount\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mchar\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtext\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mchar\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mletters\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mNameError\u001b[0m: name 'string' is not defined"
+ ]
+ }
+ ],
+ "source": [
+ "def letter_count(text, letters=string.ascii_letters):\n",
+ " letters = frozenset(letters)\n",
+ " count = 0\n",
+ " for char in text:\n",
+ " if char in letters:\n",
+ " count += 1\n",
+ " return count"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Здесь при помощи синтаксиса `parameter=default` было определено\n",
+ "значение по умолчанию для параметра `letters`. Это позволяет вызывать \n",
+ "функцию `letter_count()` с единственным аргументом, например,\n",
+ "`letter_count(\"Maggie and Hopey\")`. В этом случае внутри функции\n",
+ "параметр `letter` будет содержать строку, которая была задана как\n",
+ "значение по умолчанию. Но за нами сохраняется возможность изменить\n",
+ "значение по умолчанию, например, указав дополнительный позиционный\n",
+ "аргумент: `letter_count(\"Maggie and Hopey\", \"aeiouAEIOU\")`, или\n",
+ "используя именованный аргумент (об именованных аргументах\n",
+ "рассказывается ниже): `letter_count(\"Maggie and Hopey\", letters=\"aeiouAEIOU\")`. \n",
+ "\n",
+ "Синтаксис параметров не позволяет указывать параметры, не имеющие\n",
+ "значений по умолчанию, после параметров со значениями по умолчанию,\n",
+ "поэтому такое определение: `def bad(a, b=1, c):`, будет вызывать\n",
+ "синтаксическую ошибку. С другой стороны, мы не обязаны передавать\n",
+ "аргументы в том порядке, в каком они указаны в определении функции –\n",
+ "мы можем использовать именованные аргументы и передавать их в виде\n",
+ "`name=value`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Распаковывание аргументов и параметров\n",
+ "\n",
+ "\n",
+ "В предыдущей главе мы видели, что для передачи позиционных аргументов\n",
+ "можно использовать оператор распаковывания последовательностей\n",
+ "(`*`). Например, если возникает необходимость вычислить площадь\n",
+ "треугольника, а длины всех его сторон хранятся в списке, то мы могли\n",
+ "бы вызвать функцию так: `heron(sides[0], sides[1], sides[2])`, или\n",
+ "просто распаковать список и сделать вызов намного проще:\n",
+ "`heron(*sides)`. Если элементов в списке (или в другой\n",
+ "последовательности) больше, чем параметров в функции, мы можем\n",
+ "воспользоваться операцией извлечения среза, чтобы извлечь нужное число\n",
+ "аргументов.\n",
+ "\n",
+ "Мы можем также использовать оператор распаковывания последовательности\n",
+ "в списке параметров функции. Это удобно, когда необходимо создать\n",
+ "функцию, которая может принимать переменное число позиционных\n",
+ "аргументов. Ниже приводится функция `product()`, которая \n",
+ "вычисляет произведение своих аргументов:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def product(*args):\n",
+ " result = 1\n",
+ " for arg in args:\n",
+ " result *= arg\n",
+ " return result"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Эта функция имеет единственный аргумент с именем `args`. Наличие\n",
+ "символа `*` перед ним означает, что внутри функции параметр `args`\n",
+ "обретает форму кортежа, значениями элементов которого будут значения\n",
+ "переданных аргументов. Ниже приводятся несколько примеров вызова\n",
+ "функции:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "product(1, 2, 3, 4) # args == (1, 2, 3, 4); вернет: 24\n",
+ "product(5, 3, 8) # args == (5, 3, 8); вернет: 120\n",
+ "product(11) \t # args == (11,); вернет: 11"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Мы можем использовать именованные аргументы вслед за позиционными, как\n",
+ "в функции, которая приводится ниже, вычисляющей сумму своих\n",
+ "аргументов, каждый из которых возводится в заданную степень:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def sum_of_powers(*args, power=1):\n",
+ " result = 0\n",
+ " for arg in args:\n",
+ " result += arg ** power\n",
+ " return result"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Эта функция может вызываться только с позиционными аргументами,\n",
+ "например: `sum_of_powers(1, 3, 5)`, или как с позиционными, так и с\n",
+ "именованным аргументами, например: `sum_of_powers(1, 3, 5, power=2)`.\n",
+ "\n",
+ "Допускается также использовать символ `*` в качестве самостоятельного\n",
+ "«параметра». В данном случае он указывает, что после символа `*` не\n",
+ "может быть других позиционных параметров, однако указание именованных\n",
+ "аргументов допускается. Ниже приводится модифицированная версия\n",
+ "функции `heron()`. На этот раз функция принимает точно три позиционных\n",
+ "аргумента и один необязательный именованный аргумент."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def heron2(a, b, c, *, units=\"meters\"):\n",
+ " s = (a + b + c) / 2\n",
+ " area = math.sqrt(s * (s - a) * (s - b) * (s - c))\n",
+ " return \"{0} {1}\".format(area, units)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Ниже приводятся несколько примеров вызовов функции:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "heron2(25, 24, 7)\t\t # вернет: '84.0 meters'\n",
+ "heron2(41, 9, 40, units=\"inches\") # вернет: '180.0 inches'\n",
+ "heron2(25, 24, 7, \"inches\")\t # ОШИБКА! Возбудит исключение TypeError"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "В третьем вызове мы попытались передать четыре позиционных аргумента,\n",
+ "но оператор `*` не позволяет этого и вызывает исключение `TypeError`.\n",
+ "\n",
+ "Поместив оператор `*` первым в списке параметров, мы тем самым\n",
+ "полностью запретим использование любых позиционных аргументов и\n",
+ "вынудим тех, кто будет вызывать ее, использовать именованные\n",
+ "аргументы. Ниже приводится пример сигнатуры такой (вымышленной)\n",
+ "функции:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def print_setup(*, paper=\"Letter\", copies=1, color=False):"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Мы можем вызывать функцию `print_setup()` без аргументов, допуская\n",
+ "использование значений по умолчанию. Или изменить некоторые или\n",
+ "все значения по умолчанию, например:\n",
+ "`print_setup(paper=\"A4\", color=True)`. Но если мы попытаемся\n",
+ "использовать позиционные аргументы, например: `print_setup(\"A4\")`,\n",
+ "будет возбуждено исключение `TypeError`.\n",
+ "\n",
+ "Так же, как мы распаковываем последовательности для заполнения\n",
+ "позиционных параметров, можно распаковывать и отображения –\n",
+ "с помощью оператора распаковывания отображений (`**`). Мы можем\n",
+ "использовать оператор `**`, чтобы передать содержимое словаря в\n",
+ "функцию `print_setup()`. Например:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "options = dict(paper=\"A4\", color=True)\n",
+ "print_setup(**options)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "В данном случае пары «ключ-значение» словаря `options` будут\n",
+ "распакованы, и каждое значение будет ассоциировано с параметром, чье \n",
+ "имя соответствует ключу этого значения. Если в словаре обнаружится\n",
+ "ключ, не совпадающий ни с одним именем параметра, будет возбуждено\n",
+ "исключение `TypeError`. Любые аргументы, для которых в словаре не\n",
+ "найдется соответствующего элемента, получат значение по умолчанию, но\n",
+ "если такие аргументы не имеют значения по умолчанию, будет возбуждено\n",
+ "исключение `TypeError`. \n",
+ "\n",
+ "Кроме того, имеется возможность использовать оператор распаковывания\n",
+ "вместе с параметрами в объявлении функции. Это позволяет создавать\n",
+ "функции, способные принимать любое число именованных аргументов. Ниже\n",
+ "приводится функция `add_person_details()`, которая принимает номер\n",
+ "карточки социального страхования и фамилию в виде позиционных\n",
+ "аргументов, а также произвольное число именованных аргументов:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def add_person_details(ssn, surname, **kwargs):\n",
+ " print(\"SSN =\", ssn)\n",
+ " print(\" surname =\", surname)\n",
+ " for key in sorted(kwargs):\n",
+ " print(\" {0} = {1}\".format(key, kwargs[key]))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Эта функция может вызываться как только с двумя позиционными\n",
+ "аргументами, так и с дополнительной информацией, например:\n",
+ "`add_person_details(83272171, \"Luther\", forename=\"Lexis\", age=47)`.\n",
+ "Такая возможность обеспечивает огромную гибкость. Конечно, мы можем\n",
+ "также одновременно принимать переменное число позиционных аргументов и\n",
+ "переменное число именованных аргументов:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [
+ {
+ "ename": "IndentationError",
+ "evalue": "expected an indented block (, line 3)",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m3\u001b[0m\n\u001b[0;31m print(\"positional argument {0} = {1}\".format(i, arg))\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m expected an indented block\n"
+ ]
+ }
+ ],
+ "source": [
+ "def print_args(*args, **kwargs):\n",
+ " for i, arg in enumerate(args):\n",
+ " print(\"positional argument {0} = {1}\".format(i, arg))\n",
+ " for key in kwargs:\n",
+ " print(\"keyword argument {0} = {1}\".format(key, kwargs[key]))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Эта функция просто выводит полученные аргументы. Она может вызываться\n",
+ "вообще без аргументов или с произвольным числом позиционных и\n",
+ "именованных аргументов."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Доступ к переменным в глобальной области видимости\n",
+ "\n",
+ "\n",
+ "Иногда бывает удобно иметь несколько глобальных переменных, доступных\n",
+ "из разных функций программы. В этом нет ничего плохого, если речь идет\n",
+ "о «константах», но в случае переменных – это не самый лучший выход,\n",
+ "хотя для коротких одноразовых программ это в некоторых случаях можно\n",
+ "считать допустимым.\n",
+ "\n",
+ "Программа [digit_names.py](src-control/digit_names.py) принимает\n",
+ "необязательный код языка (`en` или `ru`) и число в виде аргументов\n",
+ "командной строки и выводит названия всех цифр заданного числа. То есть\n",
+ "если в командной строке программе было передано число `123`, она\n",
+ "выведет `one two three`. В программе имеется три глобальные\n",
+ "переменные:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "Language = \"en\"\n",
+ "\n",
+ "ENGLISH = {0: \"zero\", 1: \"one\", 2: \"two\", 3: \"three\", 4: \"four\",\n",
+ " 5: \"five\", 6: \"six\", 7: \"seven\", 8: \"eight\", 9: \"nine\"}\n",
+ "RUSSIAN = {0: \"ноль\", 1: \"один\", 2: \"два\", 3: \"три\", 4: \"четыре\",\n",
+ " 5: \"пять\", 6: \"шесть\", 7: \"семь\", 8: \"восемь\", 9: \"девять\"}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Мы следуем соглашению, в соответствии с которым имена переменных,\n",
+ "играющих роль констант, записываются только символами верхнего\n",
+ "регистра, и установили английский язык по умолчанию.\n",
+ "\n",
+ "В некотором другом месте программы выполняется обращение к переменной\n",
+ "`Language`, и ее значение используется при выборе соответствующего\n",
+ "словаря:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def print_digits(digits):\n",
+ " dictionary = ENGLISH if Language == \"en\" else RUSSIAN\n",
+ " for digit in digits:\n",
+ " print(dictionary[int(digit)], end=\" \")\n",
+ " print()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Когда интерпретатор Python встречает имя переменной `Language` внутри\n",
+ "функции, он пытается отыскать его в локальной области видимости (в\n",
+ "области видимости функции) и не находит. Поэтому он продолжает поиск в\n",
+ "глобальной области видимости (в области видимости файла `.py`), где и\n",
+ "обнаруживает его.\n",
+ "\n",
+ "Ниже приводится содержимое функции `main()` программы. Она изменяет\n",
+ "значение переменной `Language` в случае необходимости и вызывает \n",
+ "функцию `print_digits()` для вывода результата."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def main():\n",
+ " if len(sys.argv) == 1 or sys.argv[1] in {\"-h\", \"--help\"}:\n",
+ " print(\"usage: {0} [en|ru] number\".format(sys.argv[0]))\n",
+ " sys.exit()\n",
+ "\n",
+ " args = sys.argv[1:]\n",
+ " if args[0] in {\"en\", \"ru\"}:\n",
+ " global Language\n",
+ " Language = args.pop(0)\n",
+ " print_digits(args.pop(0))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Обратите внимание на использование инструкции `global` в этой\n",
+ "функции. Эта инструкция используется для того, чтобы сообщить\n",
+ "интерпретатору, что данная переменная существует в глобальной области \n",
+ "видимости (в области видимости файла `.py`) и что операция\n",
+ "присваивания должна применяться к глобальной переменной; без этой\n",
+ "инструкции операция присваивания создаст локальную переменную с тем же\n",
+ "именем.\n",
+ "\n",
+ "> **Замечание.**\n",
+ ">\n",
+ "> Если не использовать инструкцию `global`, программа сохранит\n",
+ "> свою работоспособность, но когда интерпретатор встретит переменную\n",
+ "> `Language` в условной инструкции `if`, он попытается отыскать ее в\n",
+ "> локальной области видимости (в области видимости функции) и, не\n",
+ "> обнаружив ее, создаст новую локальную переменную с именем `Language`,\n",
+ "> оставив глобальную переменную `Language` без изменений. Эта\n",
+ "> малозаметная ошибка будет проявляться только в случае запуска\n",
+ "> программы с аргументом `ru`, потому что в этом случае будет создана\n",
+ "> новая локальная переменная `Language`, в которую будет записано\n",
+ "> значение `ru`, а глобальная переменная `Language`, которая\n",
+ "> используется функцией `print_digits()`, по-прежнему будет иметь\n",
+ "> значение `en`.\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "В сложных программах лучше вообще не использовать глобальные\n",
+ "переменные, за исключением констант, которые не требуют употребления\n",
+ "инструкции `global`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Лямбда-функции\n",
+ "\n",
+ "\n",
+ "Лямбда-функции – это функции, для создания которых используется\n",
+ "следующий синтаксис:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "lambda parameters: expression"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Часть `parameters` является необязательной, а если она\n",
+ "присутствует, то обычно представляет собой простой\n",
+ "список имен переменных, разделенных запятыми, то\n",
+ "есть позиционных аргументов, хотя при необходимости\n",
+ "допускается использовать полный синтаксис определе-\n",
+ "ния аргументов, используемый в инструкции `def`. Выражение\n",
+ "`expression` не может содержать условных инструкций или циклов (хотя\n",
+ "условные выражения являются допустимыми), а также не может содержать\n",
+ "инструкцию `return` (или `yield`). Результатом лямбда-выражения\n",
+ "является анонимная функция. Когда вызывается лямбда-функция, она\n",
+ "возвращает результат вычисления выражения `expression`. Если выражение\n",
+ "expression представляет собой кортеж, оно должно быть заключено в\n",
+ "круглые скобки.\n",
+ "\n",
+ "Ниже приводится пример простой лямбда-функции, которая добавляет (или\n",
+ "не добавляет) суффикс `s` в зависимости от того, имеет ли аргумент\n",
+ "значение 1:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "s = lambda x: \"\" if x == 1 else \"s\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Лямбда-выражение возвращает анонимную функцию, которая присваивается\n",
+ "переменной `s`. Любая (вызываемая) переменная может вызываться как\n",
+ "функция при помощи круглых скобок, поэтому после выполнения некоторой\n",
+ "операции можно при помощи функции `s()` вывести сообщение с числом\n",
+ "обработанных файлов, например:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "print(\"{0} file{1} processed\".format(count, s(count)))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Лямбда-функции часто используются в виде аргумента `key` встроенной\n",
+ "функции `sorted()` или метода `list.sort()`. Предположим, что имеется\n",
+ "список, элементами которого являются трехэлементные кортежи (номер\n",
+ "группы, порядковый номер, название), и нам необходимо отсортировать\n",
+ "этот список различными способами. Ниже приводится пример такого\n",
+ "списка:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "elements = [(2, 12, \"Mg\"), (1, 11, \"Na\"), (1, 3, \"Li\"), (2, 4, \"Be\")]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Отсортировав список, мы получим следующий результат:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "[(1, 3, 'Li'), (1, 11, 'Na'), (2, 4, 'Be'), (2, 12, 'Mg')]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Ранее, когда мы рассматривали функцию `sorted()`, то видели, что\n",
+ "имеется возможность изменить порядок сортировки, если в аргументе `key`\n",
+ "передать требуемую функцию. Например, если необходимо отсортировать\n",
+ "список не по естественному порядку: номер группы, порядковый номер и\n",
+ "название, а по порядковому номеру и названию, то мы могли бы написать\n",
+ "маленькую функцию `def ignore0(e): return e[1], e[2]` и передавать ее\n",
+ "в аргументе `key`. Но создавать в программе массу крошечных функций,\n",
+ "подобных этой, очень неудобно, поэтому часто используется\n",
+ "альтернативный подход, основанный на применении лямбда-функций:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "elements.sort(key=lambda e: (e[1], e[2]))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Здесь в качестве значения аргумента key используется выражение\n",
+ "`lambda e: (e[1], e[2])`, которому в виде аргумента e последовательно\n",
+ "передаются все трехэлементные кортежи из списка. Круглые скобки,\n",
+ "окружающие лямбда-выражение, обязательны, когда выражение является\n",
+ "кортежем и лямбда-функция создается как аргумент другой функции. Для\n",
+ "достижения того же эффекта можно было бы использовать операцию\n",
+ "извлечения среза:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "elements.sort(key=lambda e: e[1:3])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Немного более сложная версия обеспечивает возможность сортировки\n",
+ "по названию, без учета регистра символов, и порядковому номеру:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "elements.sort(key=lambda e: (e[2].lower(), e[1]))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Утверждения\n",
+ "\n",
+ "\n",
+ "Что произойдет, если функция получит аргументы, имеющие ошибочные\n",
+ "значения? Что случится, если в реализации алгоритма будет допущена\n",
+ "ошибка и вычисления будут выполнены неправильно? Самое неприятное, что\n",
+ "может произойти, – это то, что программа будет выполняться без\n",
+ "каких-либо видимых проблем, но будет давать неверные результаты. Один\n",
+ "из способов избежать таких коварных проблем состоит в том, чтобы\n",
+ "писать тесты. Другой способ состоит в том, чтобы определить\n",
+ "предварительные условия и ожидаемый конечный результат, и сообщать об\n",
+ "ошибке, если они не соответствуют друг другу. В идеале следует\n",
+ "использовать как тестирование, так и метод на основе сравнения\n",
+ "предварительных условий и ожидаемых результатов.\n",
+ "\n",
+ "Предварительные условия и ожидаемый результат можно задать с помощью\n",
+ "инструкции `assert`, которая имеет следующий синтаксис:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ }
+ },
+ "outputs": [],
+ "source": [
+ "assert boolean_expression, optional_expression"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Если выражение `boolean_expression` возвращает значение `False`,\n",
+ "возбуждается исключение `AssertionError`. Если задано необязательное\n",
+ "выражение `optional_expression`, оно будет использовано в качестве\n",
+ "аргумента исключения `AssertionError`, что удобно для передачи сообщений\n",
+ "об ошибках. Однако следует отметить, что утверждения предназначены \n",
+ "для использования разработчиками, а не конечными пользователями.\n",
+ "Проблемы, возникающие в процессе нормальной эксплуатации программы,\n",
+ "такие как отсутствующие файлы или ошибочные аргументы командной\n",
+ "строки, должны обрабатываться другими средствами, например,\n",
+ "посредством вывода сообщений об ошибках или записи сообщений в файл\n",
+ "журнала.\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ ""
+ ]
+ }
+ ],
+ "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.8.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/fig-control/tryext_1.png b/fig-control/tryext_1.png
new file mode 100644
index 0000000..198f2f6
Binary files /dev/null and b/fig-control/tryext_1.png differ
diff --git a/src-control/digit_names.py b/src-control/digit_names.py
new file mode 100755
index 0000000..8e04e52
--- /dev/null
+++ b/src-control/digit_names.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+import sys
+
+#Start 1st block
+Language = "en"
+
+ENGLISH = {0: "zero", 1: "one", 2: "two", 3: "three", 4: "four",
+ 5: "five", 6: "six", 7: "seven", 8: "eight", 9: "nine"}
+RUSSIAN = {0: "ноль", 1: "один", 2: "два", 3: "три", 4: "четыре",
+ 5: "пять", 6: "шесть", 7: "семь", 8: "восемь", 9: "девять"}
+#End 1st block
+
+def main():
+ if len(sys.argv) == 1 or sys.argv[1] in {"-h", "--help"}:
+ print("usage: {0} [en|ru] number".format(sys.argv[0]))
+ sys.exit()
+
+ args = sys.argv[1:]
+ if args[0] in {"en", "ru"}:
+ global Language
+ Language = args.pop(0)
+ print_digits(args.pop(0))
+
+
+def print_digits(digits):
+ dictionary = ENGLISH if Language == "en" else RUSSIAN
+ for digit in digits:
+ print(dictionary[int(digit)], end=" ")
+ print()
+
+
+main()