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", + "![Порядок выполнения конструкции `try ... except ... finally`](fig-control/tryext_1.png)\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()