1454 lines
66 KiB
Plaintext
1454 lines
66 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"<!-- dom:TITLE: Управляющие структуры и функции -->\n",
|
||
"# Управляющие структуры и функции\n",
|
||
"<!-- dom:AUTHOR: С.В. Лемешевский Email:sergey.lemeshevsky@gmail.com at Институт математики НАН Беларуси -->\n",
|
||
"<!-- Author: --> \n",
|
||
"**С.В. Лемешевский** (email: `sergey.lemeshevsky@gmail.com`), Институт математики НАН Беларуси\n",
|
||
"\n",
|
||
"Date: **Mar 18, 2020**\n",
|
||
"\n",
|
||
"<!-- Common Mako variable and functions -->\n",
|
||
"<!-- -*- coding: utf-8 -*- -->\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"Рассматриваться управляющие структуры языка Python: условные\n",
|
||
"инструкции и циклы, инструкции обработки исключительных ситуаций.\n",
|
||
"Кроме того рассматривается создание собственных функций, и здесь будет\n",
|
||
"подробно рассматриваться чрезвычайно гибкий механизм работы с\n",
|
||
"аргументами функций."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Управляющие структуры\n",
|
||
"<div id=\"control:ifcycle\"></div>\n",
|
||
"\n",
|
||
"В языке Python условное ветвление реализуется с помощью инструкции\n",
|
||
"`if`, а циклическая обработка – с помощью инструкций `while` и `for\n",
|
||
"... in`. В языке Python имеется также такая конструкция, как условное\n",
|
||
"выражение – вариант инструкции `if`, аналог трехместного оператора\n",
|
||
"(`?:`), имеющегося в C-подобных языках."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Условное ветвление\n",
|
||
"<div id=\"control:ifcycle:if\"></div>\n",
|
||
"\n",
|
||
"Общий синтаксис инструкции условного ветвления в языке Python имеет\n",
|
||
"следующий вид:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"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",
|
||
"<div id=\"control:ifcycle:cycle\"></div>\n",
|
||
"\n",
|
||
"В языке Python есть две инструкции циклов – `while` и `for ... in`."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Циклы `while`\n",
|
||
"\n",
|
||
"<div id=\"control:ifcycle:cycle:while\"></div>\n",
|
||
"\n",
|
||
"Ниже приводится полный синтаксис цикла `while`:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"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": null,
|
||
"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",
|
||
"<div id=\"control:ifcycle:cycle:for\"></div>\n",
|
||
"\n",
|
||
"Подобно циклу `while`, полный синтаксис цикла `for ... in` также\n",
|
||
"включает необязательное предложение `else`:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"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": null,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"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",
|
||
"<!-- Local Variables: -->\n",
|
||
"<!-- doconce-chapter-nickname: \"control\" -->\n",
|
||
"<!-- doconce-section-nickname: \"ifcycle\" -->\n",
|
||
"<!-- End: -->"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Обработка исключений\n",
|
||
"<div id=\"control:except\"></div>\n",
|
||
"\n",
|
||
"Об ошибках и исключительных ситуациях интерпретатор Python сообщает\n",
|
||
"посредством возбуждения исключений, хотя в некоторых библиотеках\n",
|
||
"сторонних разработчиков еще используются устаревшие приемы, такие как\n",
|
||
"возврат «ошибочного» значения."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Перехват и возбуждение исключений\n",
|
||
"<div id=\"control:except:tryexept\"></div>\n",
|
||
"\n",
|
||
"Перехватывать исключения можно с помощью блоков `try ... except`,\n",
|
||
"которые имеют следующий синтаксис:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"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",
|
||
"<!-- dom:FIGURE: [fig-control/tryext_1.png, width=800 frac=1.0] Порядок выполнения конструкции `try ... except ... finally` <div id=\"control:except:fig:1\"></div> -->\n",
|
||
"<!-- begin figure -->\n",
|
||
"<div id=\"control:except:fig:1\"></div>\n",
|
||
"<!-- end figure -->\n",
|
||
"\n",
|
||
"\n",
|
||
"Ниже приводится окончательная версия функции `list_find()`, на этот\n",
|
||
"раз она использует механизм обработки исключения:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"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",
|
||
"<div id=\"control:except:tryexept:1\"></div>\n",
|
||
"\n",
|
||
"Исключения представляют собой удобное средство управления потоком\n",
|
||
"выполнения. Мы можем воспользоваться этим, используя либо встроенные\n",
|
||
"исключения, либо создавая свои собственные и возбуждая нужные нам,\n",
|
||
"когда это необходимо. Возбудить исключение можно одним из двух\n",
|
||
"способов:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"raise exception(args)\n",
|
||
"raise"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"В первом случае, то есть когда явно указывается возбуждаемое\n",
|
||
"исключение, оно должно быть либо встроенным, либо нашим собственным,\n",
|
||
"наследующим класс `Exception`. Если исключению в виде аргумента\n",
|
||
"передается некоторый текст, этот текст будет выведен на экран, если\n",
|
||
"исключение не будет обработано программой. Во втором случае, то есть \n",
|
||
"когда исключение не указывается, инструкция `raise` повторно возбудит\n",
|
||
"текущее активное исключение, а в случае отсутствия активного\n",
|
||
"исключения будет возбуждено исключение `TypeError`. \n",
|
||
"\n",
|
||
"\n",
|
||
"<!-- Local Variables: -->\n",
|
||
"<!-- doconce-chapter-nickname: \"control\" -->\n",
|
||
"<!-- doconce-section-nickname: \"except\" -->\n",
|
||
"<!-- End: -->"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Собственные функции\n",
|
||
"<div id=\"control:funcs\"></div>\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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"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",
|
||
"<div id=\"control:funcs:unpack\"></div>\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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"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",
|
||
"<div id=\"control:funcs:global\"></div>\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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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",
|
||
"<div id=\"control:funcs:lambda\"></div>\n",
|
||
"\n",
|
||
"Лямбда-функции – это функции, для создания которых используется\n",
|
||
"следующий синтаксис:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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": null,
|
||
"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",
|
||
"<div id=\"control:funcs:assert\"></div>\n",
|
||
"\n",
|
||
"Что произойдет, если функция получит аргументы, имеющие ошибочные\n",
|
||
"значения? Что случится, если в реализации алгоритма будет допущена\n",
|
||
"ошибка и вычисления будут выполнены неправильно? Самое неприятное, что\n",
|
||
"может произойти, – это то, что программа будет выполняться без\n",
|
||
"каких-либо видимых проблем, но будет давать неверные результаты. Один\n",
|
||
"из способов избежать таких коварных проблем состоит в том, чтобы\n",
|
||
"писать тесты. Другой способ состоит в том, чтобы определить\n",
|
||
"предварительные условия и ожидаемый конечный результат, и сообщать об\n",
|
||
"ошибке, если они не соответствуют друг другу. В идеале следует\n",
|
||
"использовать как тестирование, так и метод на основе сравнения\n",
|
||
"предварительных условий и ожидаемых результатов.\n",
|
||
"\n",
|
||
"Предварительные условия и ожидаемый результат можно задать с помощью\n",
|
||
"инструкции `assert`, которая имеет следующий синтаксис:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"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",
|
||
"<!-- Local Variables: -->\n",
|
||
"<!-- doconce-chapter-nickname: \"control\" -->\n",
|
||
"<!-- doconce-section-nickname: \"funcs\" -->\n",
|
||
"<!-- End: -->\n",
|
||
"\n",
|
||
"\n",
|
||
"<!-- Local Variables: -->\n",
|
||
"<!-- doconce-chapter-nickname: \"control\" -->\n",
|
||
"<!-- End: -->"
|
||
]
|
||
}
|
||
],
|
||
"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
|
||
}
|