3018 lines
173 KiB
Plaintext
3018 lines
173 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",
|
||
"Рассматриваются кортежи и списки, а также новые типы коллекций, включая словари и множества."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Последовательности\n",
|
||
"<div id=\"collections:seq\"></div>\n",
|
||
"\n",
|
||
"Последовательности – это один из типов данных, поддерживающих\n",
|
||
"оператор проверки на вхождение (`in`), функцию определения размера\n",
|
||
"(`len()`), оператор извлечения срезов (`[]`) и возможность выполнения\n",
|
||
"итераций. В языке Python имеется пять встроенных типов\n",
|
||
"последовательностей: `bytearray`, `bytes`, `list`, `str` и\n",
|
||
"`tuple`. Ряд дополнительных типов последовательностей реализован в\n",
|
||
"стандартной библиотеке; наиболее примечательным \n",
|
||
"из них является тип `collections.namedtuple`. При выполнении итераций\n",
|
||
"все эти последовательности гарантируют строго определенный порядок\n",
|
||
"следования элементов.\n",
|
||
"\n",
|
||
"Строки мы уже рассматривали выше, а в этом разделе познакомимся с\n",
|
||
"кортежами, именованными кортежами и списками."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Кортежи\n",
|
||
"<div id=\"collections:seq:tuples\"></div>\n",
|
||
"\n",
|
||
"Кортеж – это упорядоченная последовательность из нуля или более ссылок\n",
|
||
"на объекты. Кортежи поддерживают тот же синтаксис получения срезов,\n",
|
||
"что и строки. Это упрощает извлечение элементов из кортежа. Подобно\n",
|
||
"строкам, кортежи относятся к категории неизменяемых объектов, поэтому\n",
|
||
"мы не можем замещать или удалять какие-либо их элементы. Если нам\n",
|
||
"необходимо иметь возможность изменять упорядоченную\n",
|
||
"последовательность, то вместо кортежей можно просто использовать\n",
|
||
"списки или, если в программе уже используется кортеж, который\n",
|
||
"нежелательно модифицировать, можно преобразовать кортеж в список с\n",
|
||
"помощью функции преобразования `list()` и затем изменять полученный\n",
|
||
"список.\n",
|
||
"\n",
|
||
"Тип данных `tuple` может вызываться как функция `tuple()` – без\n",
|
||
"аргументов она возвращает пустой кортеж, с аргументом типа tuple\n",
|
||
"возвращает поверхностную копию аргумента; в случае, если аргумент\n",
|
||
"имеет другой тип, выполняется попытка преобразовать его в объект\n",
|
||
"типа `tuple`. Эта функция принимает не более одного аргумента. Кроме\n",
|
||
"того, кортежи могут создаваться без использования функции\n",
|
||
"`tuple()`. Пустой кортеж создается с помощью пары пустых круглых\n",
|
||
"скобок `()`, а кортеж, состоящий из одного или более элементов, может быть\n",
|
||
"создан с помощью запятых. Иногда кортежи приходится заключать в\n",
|
||
"круглые скобки, чтобы избежать синтаксической\n",
|
||
"неоднозначности. Например, чтобы передать кортеж `1, 2, 3` в функцию,\n",
|
||
"необходимо использовать такую форму записи: `function((1, 2, 3))`.\n",
|
||
"\n",
|
||
"<!-- dom:FIGURE: [fig-collections/seq_1.png, width=800 frac=1.0] Позиции элементов в кортеже <div id=\"collections:seq:fig:1\"></div> -->\n",
|
||
"<!-- begin figure -->\n",
|
||
"<div id=\"collections:seq:fig:1\"></div>\n",
|
||
"\n",
|
||
"<!-- end figure -->\n",
|
||
"\n",
|
||
"\n",
|
||
"На рис. [Позиции элементов в кортеже](#collections:seq:fig:1) показан кортеж\n",
|
||
"`t = \"venus\", – 28, \"green\", \"21\", 19.74` и индексы элементов внутри кортежа. Строки\n",
|
||
"индексируются точно так же, но, если в строках каждой позиции\n",
|
||
"соответствует единственный символ, то в кортежах каждой позиции\n",
|
||
"соответствует единственная ссылка на объект.\n",
|
||
"\n",
|
||
"Кортежи предоставляют всего два метода: `t.count(x)`, который\n",
|
||
"возвращает количество объектов `x` в кортеже `t`, и `t.index(x)`,\n",
|
||
"который возвращает индекс самого первого (слева) вхождения объекта `x`\n",
|
||
"в кортеж `t` или возбуждает исключение `ValueError`, если объект `x`\n",
|
||
"отсутствует в кортеже. (Эти методы имеются также и у списков.)\n",
|
||
"\n",
|
||
"Кроме того, кортежи могут использоваться с оператором `+`\n",
|
||
"(конкатенации), `*` (дублирования) и `[]` (получения среза), а\n",
|
||
"операторы `in` и `not in` могут применяться для проверки на\n",
|
||
"вхождение. Можно использовать также комбинированные операторы\n",
|
||
"присваивания `+=` и `*=`. Несмотря на то, что кортежи являются\n",
|
||
"неизменяемыми объектами, при выполнении этих операторов интерпретатор\n",
|
||
"Python создает за кулисами новый кортеж с результатом операции и\n",
|
||
"присваивает ссылку на него объекту, расположенному слева от оператора,\n",
|
||
"то есть используется тот же самый прием, что и со строками. Кортежи\n",
|
||
"могут сравниваться с помощью стандартных операторов сравнения (`<`,\n",
|
||
"`<=`, `==`, `!=`, `>=`, `>`), при этом сравнивание производится\n",
|
||
"поэлементно (и рекурсивно, при наличии вложенных элементов, таких как\n",
|
||
"кортежи в кортежах).\n",
|
||
"\n",
|
||
"Рассмотрим несколько примеров получения срезов, начав с извлечения\n",
|
||
"единственного элемента и группы элементов:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 1,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"hair = \"black\", \"brown\", \"blonde\", \"red\""
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"hair[2]"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"hair[-3:] # то же, что и hair[1:]"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Эта операция выполняется точно так же, как и в случае со строками,\n",
|
||
"списками или любыми другими последовательностями."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"hair[:2], \"gray\", hair[2:]"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Здесь мы попытались создать новый кортеж из 5 элементов, но в\n",
|
||
"результате получили кортеж с тремя элементами, содержащий два\n",
|
||
"двухэлементных кортежа. Это произошло потому, что мы применили\n",
|
||
"оператор запятой к трем элементам (кортеж, строка и кортеж). Чтобы\n",
|
||
"получить единый кортеж со всеми этими элементами, необходимо выполнить\n",
|
||
"конкатенацию кортежей:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"hair[:2] + (\"gray\",) + hair[2:]"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Чтобы создать кортеж из одного элемента, необходимо поставить запятую,\n",
|
||
"но если запятую просто добавить, будет получено исключение `TypeError`\n",
|
||
"(так как интерпретатор будет думать, что выполняется конкатенация\n",
|
||
"строки и кортежа), поэтому необходимо использовать запятую и круглые\n",
|
||
"скобки.\n",
|
||
"\n",
|
||
"Коллекции допускают возможность вложения с любой глубиной\n",
|
||
"вложенности. Оператор извлечения срезов `[]` может применяться для\n",
|
||
"доступа к вложенным коллекциям столько раз, сколько это будет\n",
|
||
"необходимо. Например:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"things = (1, -7.5, (\"pea\", (5, \"Xyz\"), \"queue\"))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"things[2][1][1][2]"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Кортежи могут хранить элементы любых типов, включая другие коллекции,\n",
|
||
"такие как кортежи и списки, так как на самом деле кортежи хранят\n",
|
||
"ссылки на объекты. Использование сложных, вложенных структур данных,\n",
|
||
"таких, как показано ниже, легко может создавать путаницу. Одно из\n",
|
||
"решений этой проблемы состоит в том, чтобы давать значениям индексов\n",
|
||
"осмысленные имена. Например:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 8,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"MANUFACTURER, MODEL, SEATING = (0, 1, 2)\n",
|
||
"MINIMUM, MAXIMUM = (0, 1)\n",
|
||
"aircraft = (\"Airbus\", \"A320-200\", (100, 220))\n",
|
||
"aircraft[SEATING][MAXIMUM]"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"В первых двух строках вышеприведенного фрагмента мы выполнили\n",
|
||
"присваивание кортежам. Когда справа от оператора присваивания\n",
|
||
"указывается последовательность (в данном случае – это кортежи),\n",
|
||
"а слева указан кортеж, мы говорим, что последовательность справа\n",
|
||
"*распаковывается*. Операция распаковывания последовательностей\n",
|
||
"может использоваться для организации обмена значений между\n",
|
||
"переменными, например:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"a, b = (b, a)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Именованные кортежы\n",
|
||
"<div id=\"collections:seq:namedtuple\"></div>\n",
|
||
"\n",
|
||
"Именованные кортежи ведут себя точно так же, как и обычные кортежи, и\n",
|
||
"не уступают им в производительности. Отличаются они возможностью\n",
|
||
"ссылаться на элементы кортежа не только по числовому индексу, но и по\n",
|
||
"имени, что в свою очередь позволяет создавать сложные агрегаты из\n",
|
||
"элементов данных.\n",
|
||
"\n",
|
||
"В модуле `collections` имеется функция `namedtuple()`. Эта функция\n",
|
||
"используется для создания собственных типов кортежей. Например:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 10,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"import collections\n",
|
||
"Sale = collections.namedtuple(\"Sale\", \"productid customerid date quantity price\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Первый аргумент функции `collections.namedtuple()` – это имя\n",
|
||
"создаваемого кортежа. Второй аргумент – это строка имен, разделенных\n",
|
||
"пробелами, для каждого элемента, который будет присутствовать в этом\n",
|
||
"кортеже. Первый аргумент и имена во втором аргументе должны быть\n",
|
||
"допустимыми идентификаторами языка Python. Функция возвращает класс\n",
|
||
"(тип данных), который может использоваться для создания именованных\n",
|
||
"кортежей. Так, в примере выше мы можем интерпретировать имя `Sale` как\n",
|
||
"имя любого другого класса (такого как `tuple`) в языке Python и\n",
|
||
"создавать объекты типа `Sale`. Например:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 11,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"sales = []\n",
|
||
"sales.append(Sale(432, 921, \"2008-09-14\", 3, 7.99))\n",
|
||
"sales.append(Sale(419, 874, \"2008-09-15\", 1, 18.49))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"В этом примере мы создали список из двух элементов типа `Sale`, то\n",
|
||
"есть из двух именованных кортежей. Мы можем обращаться к элементам\n",
|
||
"таких кортежей по их индексам – например, обратиться к элементу\n",
|
||
"`price` в первом элементе списка `sales` можно с помощью выражения\n",
|
||
"`sales[0][-1]` (вернет значение `7.99`) – или по именам, которые\n",
|
||
"делают программный код более удобочитаемым:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 12,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"total = 0\n",
|
||
"for sale in sales:\n",
|
||
" total += sale.quantity*sale.price\n",
|
||
"print(\"Total {0:.2f}\".format(total))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Очень часто простоту и удобство, которые предоставляют именованные\n",
|
||
"кортежи, можно обратить на пользу делу. Например, ниже приводится\n",
|
||
"версия примера `aircraft` из предыдущего подраздела, имеющая более\n",
|
||
"аккуратный вид:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 13,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"Aircraft = collections.namedtuple(\"Aircraft\", \"manufacturer model seating\")\n",
|
||
"Seating = collections.namedtuple(\"Seating\", \"minimum maximum\")\n",
|
||
"aircraft = Aircraft(\"Airbus\", \"A320-200\", Seating(100, 220))\n",
|
||
"aircraft.seating.maximum"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Списки\n",
|
||
"<div id=\"collections:seq:lists\"></div>\n",
|
||
"\n",
|
||
"Список – это упорядоченная последовательность из нуля\n",
|
||
"или более ссылок на объекты. Списки поддерживают тот\n",
|
||
"же синтаксис получения срезов, что и строки с кортежами. Это упрощает\n",
|
||
"извлечение элементов из списка. В отличие от строк и кортежей списки\n",
|
||
"относятся к категории изменяемых объектов, поэтому мы можем замещать\n",
|
||
"или удалять любые их элементы. Кроме того, существует возможность\n",
|
||
"вставлять, замещать и удалять целые срезы списков.\n",
|
||
"\n",
|
||
"Тип данных `list` может вызываться как функция `list()` –\n",
|
||
"без аргументов она возвращает пустой список, с аргументом типа `list`\n",
|
||
"возвращает поверхностную копию аргумента; в случае, если аргумент\n",
|
||
"имеет другой тип, выполняется попытка преобразовать его в объект типа\n",
|
||
"`list`. Эта функция принимает не более одного аргумента. Кроме того,\n",
|
||
"списки могут создаваться без использования функции `list()`. Пустой\n",
|
||
"список создается с помощью пары пустых квадратных скобок `[]`, а список,\n",
|
||
"состоящий из одного или более элементов, может быть создан с помощью\n",
|
||
"последовательности элементов, разделенных запятыми, заключенной в\n",
|
||
"квадратные скобки. Другой способ создания списков заключается в\n",
|
||
"использовании генераторов списков – эта тема будет рассматриваться\n",
|
||
"ниже в этом подразделе.\n",
|
||
"\n",
|
||
"Поскольку все элементы списка в действительности являются ссылками на\n",
|
||
"объекты, списки, как и кортежи, могут хранить элементы любых типов\n",
|
||
"данных, включая коллекции, такие как списки и кортежи. Списки могут\n",
|
||
"сравниваться с помощью стандартных операторов сравнения (`<`, `<=`,\n",
|
||
"`==`, `!=`, `>=`, `>`), при этом сравнивание производится поэлементно\n",
|
||
"(и рекурсивно, при наличии вложенных элементов, таких как списки или\n",
|
||
"кортежи в списках).\n",
|
||
"\n",
|
||
"В результате выполнения операции присваивания"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 14,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"L = [ – 17.5, \"kilo\", 49, \"V\", [\"ram\", 5, \"echo\"], 7]"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"мы получим список, как показано на рис. [collections:seq:fig:2](#collections:seq:fig:2)\n",
|
||
"\n",
|
||
"<!-- dom:FIGURE: [fig-collections/seq_2.png, width=800 frac=1.0] Позиции элементов в списке <div id=\"collections:seq:fig:2\"></div> -->\n",
|
||
"<!-- begin figure -->\n",
|
||
"<div id=\"collections:seq:fig:2\"></div>\n",
|
||
"<!-- end figure -->\n",
|
||
"\n",
|
||
"\n",
|
||
"К спискам, таким как `L`, мы можем применять оператор извлечения\n",
|
||
"среза, повторяя его столько раз, сколько потребуется для доступа к\n",
|
||
"элементам в списке, как показано ниже:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"ename": "NameError",
|
||
"evalue": "name 'L' 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<ipython-input-2-bdf2b9da3859>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m6\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m17.5\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'kilo'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'k'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'echo'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'c'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
|
||
"\u001b[0;31mNameError\u001b[0m: name 'L' is not defined"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"L[0] == L[-6] == -17.5\n",
|
||
"L[1] == L[-5] == 'kilo'\n",
|
||
"L[1][0] == L[-5][0] == 'k'\n",
|
||
"L[4][2] == L[4][-1] == L[-2][2] == L[-2][-1] == 'echo'\n",
|
||
"L[4][2][1] == L[4][2][-3] == L[-2][-1][1] == L[-2][-1][-3] == 'c'"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Списки, как и кортежи, могут вкладываться друг в друга; допускают\n",
|
||
"выполнение итераций по их элементам и извлечение срезов. Все примеры с\n",
|
||
"кортежами, которые приводились в предыдущем подразделе, будут работать\n",
|
||
"точно так же, если вместо кортежей в них будут использованы\n",
|
||
"списки. Списки поддерживают операторы проверки на вхождение `in` и\n",
|
||
"`not in`, оператор конкатенации `+`, оператор расширения `+=` (то есть\n",
|
||
"добавляет операнд справа в конец списка) и операторы дублирования `*`\n",
|
||
"и `*=`. Списки могут также использоваться в качестве аргументов\n",
|
||
"функции `len()`.\n",
|
||
"\n",
|
||
"Кроме того, списки предоставляют методы, перечисленные в\n",
|
||
"табл. [1](#collections:seq:tbl:1) "
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Таблица 1 : Методы списков <div id=\"collections:seq:tbl:1\"></div>\n",
|
||
"\n",
|
||
"\n",
|
||
"<table border=\"1\">\n",
|
||
"<thead>\n",
|
||
"<tr><th align=\"left\"> Синтаксис </th> <th align=\"left\"> Описание </th> </tr>\n",
|
||
"</thead>\n",
|
||
"<tbody>\n",
|
||
"<tr><td align=\"left\"> <code>L.append(x)</code> </td> <td align=\"left\"> Добавляет элемент <code>x</code> в конец списка <code>L</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>L.count(x)</code> </td> <td align=\"left\"> Возвращает число вхождений элемента <code>x</code> в список <code>L</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>L.extend(m)</code> или <code>L += m</code> </td> <td align=\"left\"> Добавляет в конец списка L все элементы итерируемого объекта <code>m</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>L.index(x, start, end)</code> </td> <td align=\"left\"> Возвращает индекс самого первого (слева) вхождения элемента <code>x</code> в список <code>L</code> (или в срез <code>start:end</code> списка <code>L</code>); в противном случае возбуждает исключение <code>ValueError</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>L.insert(i, x)</code> </td> <td align=\"left\"> Вставляет элемент <code>x</code> в список <code>L</code> в позицию <code>int i</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>L.pop()</code> </td> <td align=\"left\"> Удаляет самый последний элемент из списка <code>L</code> и возвращает его в качестве результата </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>L.pop(i)</code> </td> <td align=\"left\"> Удаляет из списка <code>L</code> элемент с индексом <code>int i</code> и возвращает его в качестве результата </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>L.remove(x)</code> </td> <td align=\"left\"> Удаляет самый первый (слева) найденный элемент <code>x</code> из списка <code>L</code> или возбуждает исключение <code>ValueError</code>, если элемент <code>x</code>не будет найден <code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> </code>L.reverse()<code> </td> <td align=\"left\"> Переставляет в памяти элементы списка в обратном порядке </td> </tr>\n",
|
||
"<tr><td align=\"left\"> </code>L.sort()<code> </td> <td align=\"left\"> Сортирует список в памяти. Этот метод принимает те же необязательные аргументы </code>key<code> и </code>reverse<code> что и встроенная функция </code>sorted()` </td> </tr>\n",
|
||
"</tbody>\n",
|
||
"</table>\n",
|
||
"\n",
|
||
"\n",
|
||
"Несмотря на то, что для доступа к элементам списка можно использовать\n",
|
||
"оператор извлечения среза, тем не менее в некоторых ситуациях бывает\n",
|
||
"необходимо одновременно извлечь две или более частей списка. \n",
|
||
"Сделать это можно с помощью операции распаковывания\n",
|
||
"последовательности. Любой итерируемый объект (списки, кортежи и\n",
|
||
"другие) может быть распакован с помощью оператора распаковывания\n",
|
||
"«звездочка» (`*`). Когда слева от оператора присваивания указывается\n",
|
||
"две или более переменных, одна из которых предваряется символом `*`,\n",
|
||
"каждой переменной присваивается по одному элементу списка, а\n",
|
||
"переменной со звездочкой присваивается оставшаяся часть списка. Ниже\n",
|
||
"приводится несколько примеров выполнения распаковывания списков:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 16,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"first, *rest = [9, 2, -4, 8, 7]\n",
|
||
"first, rest"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 17,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"first, *mid, last = \"Charles Philip Arthur George Windsor\".split()\n",
|
||
"first, mid, last"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 18,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"*directories, executable = \"/usr/local/bin/gvim\".split(\"/\")\n",
|
||
"directories, executable"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Когда используется оператор распаковывания последовательности,\n",
|
||
"как в данном примере, выражение `*rest` и подобные ему называются\n",
|
||
"*выражениями со звездочкой*.\n",
|
||
"\n",
|
||
"В языке Python имеется также похожее понятие *аргументов со звездочкой*.\n",
|
||
"Например, допустим, что имеется следующая функция, принимающая три\n",
|
||
"аргумента:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 19,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def product(a, b, c):\n",
|
||
" return a * b * c"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"тогда мы можем вызывать эту функцию с тремя аргументами или\n",
|
||
"использовать аргументы со звездочкой:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 20,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"product(2, 3, 5)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 21,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"L = [2, 3, 5]\n",
|
||
"product(*L)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 22,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"product(2, *L[1:])"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"В первом примере функция вызывается, как обычно, с тремя\n",
|
||
"аргументами. Во втором вызове использован аргумент со звездочкой; в\n",
|
||
"этом случае список из трех элементов распаковывается оператором `*`,\n",
|
||
"так что функция получает столько аргументов, сколько ей\n",
|
||
"требуется. Того же эффекта можно было бы добиться при использовании\n",
|
||
"кортежа с тремя элементами. В третьем вызове функции первый аргумент\n",
|
||
"передается традиционным способом, а другие два – посредством\n",
|
||
"применения операции распаковывания двухэлементного среза списка `L`. \n",
|
||
"\n",
|
||
"Имеется возможность выполнять итерации по элементам списка с помощью\n",
|
||
"конструкции `for item in L:`. Если в цикле потребуется изменять\n",
|
||
"элементы списка, то можно использовать следующий прием:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 23,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"for i in range(len(L)):\n",
|
||
" L[i] = process(L[i])"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"> **Замечание.**\n",
|
||
">\n",
|
||
"> Встроенная функция `range()` возвращает целочисленный\n",
|
||
"> итератор. С одним целочисленным аргументом, `n`, итератор `range()`\n",
|
||
"> возвращает последовательность чисел $0$, $1$, ..., $n-1$.\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"Этот прием можно использовать для увеличения всех элементов в списке\n",
|
||
"целых чисел. Например:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 24,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"for i in range(len(numbers)):\n",
|
||
" numbers[i] += 1"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Генераторы списков\n",
|
||
"\n",
|
||
"<div id=\"collections:seq:lists:gen\"></div>\n",
|
||
"\n",
|
||
"Небольшие списки часто создаются как литералы, но длинные списки\n",
|
||
"обычно создаются программным способом. Списки целых чисел могут\n",
|
||
"создаваться с помощью выражения `list(range(n))`; когда необходим\n",
|
||
"итератор целых чисел, достаточно функции `range()`; а для создания\n",
|
||
"списков других типов часто используется оператор цикла `for ... in`.\n",
|
||
"Предположим, например, что нам требуется получить список високосных\n",
|
||
"годов в определенном диапазоне. Для начала мы могли бы использовать\n",
|
||
"такой цикл:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 25,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"leaps = []\n",
|
||
"for year in range(1900, 1940):\n",
|
||
" if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):\n",
|
||
" leaps.append(year)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Когда функции `range()` передаются два целочисленных\n",
|
||
"аргумента `n` и `m`, итератор возвращает последовательность целых\n",
|
||
"чисел `n`, `n+1`, ..., `m–1`. \n",
|
||
"\n",
|
||
"Конечно, если диапазон известен заранее, можно было\n",
|
||
"бы использовать литерал списка, например,\n",
|
||
"`leaps = [1904, 1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936]`.\n",
|
||
"\n",
|
||
"*Генератор списков* – это выражение и цикл с дополнительным условием,\n",
|
||
"заключенное в квадратные скобки, в котором цикл используется для\n",
|
||
"создания элементов списка, а условие используется для исключения\n",
|
||
"нежелательных элементов. В простейшем виде генератор списков\n",
|
||
"записывается, как показано ниже:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"```Python\n",
|
||
" [item for item in iterable]\n",
|
||
"```"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Это выражение вернет список всех элементов объекта `iterable` и\n",
|
||
"семантически ничем не отличается от выражения\n",
|
||
"`list(iterable)`. Интересными генераторы списков делают две\n",
|
||
"особенности – они могут использоваться как выражения и они допускают\n",
|
||
"включение условной инструкции, вследствие чего мы получаем две\n",
|
||
"типичные синтаксические конструкции использования генераторов списков:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"```Python\n",
|
||
" [expression for item in iterable]\n",
|
||
" [expression for item in iterable if condition]\n",
|
||
"```"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Вторая форма записи эквивалентна циклу:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"```Python\n",
|
||
" temp = []\n",
|
||
" for item in iterable:\n",
|
||
" if condition:\n",
|
||
" temp.append(expression)\n",
|
||
"```"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Обычно выражение `expression` является либо самим элементом `item`,\n",
|
||
"либо некоторым выражением с его участием. Конечно, генератору\n",
|
||
"списков не требуется временная переменная `temp[]`, которая необходима\n",
|
||
"в версии с циклом `for ... in`.\n",
|
||
"\n",
|
||
"Теперь можно переписать программный код создания списка високосных\n",
|
||
"годов с использованием генератора списка. Мы сделаем это в три\n",
|
||
"этапа. Сначала создадим список, содержащий все годы в указанном\n",
|
||
"диапазоне:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 26,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"leaps = [y for y in range(1900, 1940)]"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"То же самое можно было бы сделать с помощью выражения\n",
|
||
"`leaps = list(range(1900, 1940))`. Теперь добавим простое условие,\n",
|
||
"которое будет оставлять в списке только каждый четвертый год:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 27,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"leaps = [y for y in range(1900, 1940) if y % 4 == 0]"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"И, наконец, получаем окончательную версию:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 28,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"leaps = [y for y in range(1900, 1940)\n",
|
||
" if (y % 4 == 0 and y % 100 != 0) or (y % 400 == 0)]"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Использование генератора списков в данном случае позволило уменьшить\n",
|
||
"объем программного кода с четырех строк до двух – не так много, но в\n",
|
||
"крупных проектах суммарная экономия может оказаться весьма\n",
|
||
"существенной. \n",
|
||
"\n",
|
||
"Так как генераторы списков воспроизводят списки, то есть итерируемые\n",
|
||
"объекты, и сами генераторы списков используют итерируемые объекты,\n",
|
||
"имеется возможность вкладывать генераторы списков друг \n",
|
||
"в друга. Это эквивалентно вложению циклов `for ... in`. Например, если\n",
|
||
"бы нам потребовалось сгенерировать список всех возможных кодов одежды\n",
|
||
"для разных полов, разных размеров и расцветок, но исключая одежду для\n",
|
||
"полных женщин, нужды и чаянья которых индустрия моды нередко\n",
|
||
"игнорирует, мы могли бы использовать вложенные циклы\n",
|
||
"`for ... in`, как показано ниже:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 29,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"codes = []\n",
|
||
"for sex in \"MF\":\n",
|
||
" for size in \"SMLX\":\n",
|
||
" \tif sex == \"F\" and size\n",
|
||
"\t continue\n",
|
||
" for color in \"BGW\":\n",
|
||
"\t codes.append(sex + size + color)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Этот фрагмент воспроизводит список, содержащий 21 элемент – `['MSB',\n",
|
||
"'MSG', ..., 'FLW']`. Тот же самый список можно создать парой строк,\n",
|
||
"если воспользоваться генераторами списков:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"codes = [s + z + c for s in \"MF\" for z in \"SMLX\" for c in \"BGW\"\n",
|
||
" if not (s == \"F\" and z == \"X\")]"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Здесь каждый элемент списка воспроизводится выражением `s + z + c`.\n",
|
||
"Кроме того, в генераторе списков несколько иначе построена логика\n",
|
||
"обхода нежелательной комбинации пол/размер – проверка выполняется в\n",
|
||
"самом внутреннем цикле, тогда как в версии с циклами `for ... in` эта\n",
|
||
"проверка выполняется в среднем цикле. Любой генератор списков можно\n",
|
||
"переписать, используя один или более циклов `for ... in`. \n",
|
||
"\n",
|
||
"<!-- Local Variables: -->\n",
|
||
"<!-- doconce-chapter-nickname: \"collections\" -->\n",
|
||
"<!-- doconce-section-nickname: \"seq\" -->\n",
|
||
"<!-- End: -->"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Множества\n",
|
||
"<div id=\"collections:sets\"></div>\n",
|
||
"\n",
|
||
"Тип `set` – это разновидность коллекций, которая поддерживает оператор\n",
|
||
"проверки на вхождение `in`, функцию `len()` и относится к разряду\n",
|
||
"итерируемых объектов. Кроме того, множества предоставляют метод \n",
|
||
"`set.isdisjoint()` и поддерживают операторы сравнения и битовые\n",
|
||
"операторы (которые в контексте множеств используются для получения \n",
|
||
"объединения, пересечения и т. д.). В языке Python имеется два\n",
|
||
"встроенных типа множеств: изменяемый тип `set` и неизменяемый\n",
|
||
"`frozenset`. При переборе элементов множества элементы могут следовать\n",
|
||
"в произвольном порядке. \n",
|
||
"\n",
|
||
"В состав множеств могут включаться только *хешируемые*\n",
|
||
"объекты. Хешируемые объекты – это объекты, имеющие специальный метод \n",
|
||
"`__hash__()`, на протяжении всего жизненного цикла объекта всегда\n",
|
||
"возвращающий одно и то же значение, которые могут участвовать в\n",
|
||
"операциях сравнения на равенство посредством специального метода \n",
|
||
"`__eq__()`. (Специальные методы – это методы, имена которых начинаются\n",
|
||
"и оканчиваются двумя символами подчеркивания).\n",
|
||
"\n",
|
||
"Все встроенные неизменяемые типы данных, такие как `float`,\n",
|
||
"`frozenset`, `int`, `str` и `tuple`, являются хешируемыми объектами и\n",
|
||
"могут добавляться во множества. Встроенные изменяемые типы данных,\n",
|
||
"такие как `dict`, `list` и `set`, не являются хешируемыми объектами,\n",
|
||
"так как значение хеша в каждом конкретном случае зависит от\n",
|
||
"содержащихся в объекте элементов, поэтому они не могут добавляться в\n",
|
||
"множества.\n",
|
||
"\n",
|
||
"Множества могут сравниваться между собой с использованием стандартных\n",
|
||
"операторов сравнения (`<`, `<=`, `==`, `!=`, `>=`, `>`). Обратите\n",
|
||
"внимание: операторы `==` и `!=` имеют обычный смысл, и сравнение\n",
|
||
"выполняется путем поэлементного сравнения (или рекурсивно при наличии\n",
|
||
"таких вложенных элементов, как кортежи и фиксированные множества\n",
|
||
"(`frozenset`)), но остальные операторы сравнения выполняют сравнение\n",
|
||
"подмножеств и надмножеств, как вскоре будет показано. "
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Тип `set`\n",
|
||
"<div id=\"collections:sets:set\"></div>\n",
|
||
"\n",
|
||
"Тип `set` – это неупорядоченная коллекция из нуля или более ссылок на\n",
|
||
"объекты, указывающих на хешируемые объекты. Множества относятся к\n",
|
||
"категории изменяемых типов, поэтому легко можно добавлять и удалять их\n",
|
||
"элементы, но, так как они являются неупорядоченными коллекциями, к ним\n",
|
||
"не применимо понятие индекса и не применима операция извлечения\n",
|
||
"среза. На рис. [Множество – это неупорядоченная коллекция уникальных элементов](#collections:sets:fig:1) иллюстрируется множество,\n",
|
||
"созданное следующим фрагментом программного кода:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 31,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"S = {7, \"veil\", 0, -29, (\"x\", 11), \"sun\", frozenset({8, 4, 7}), 913}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"<!-- dom:FIGURE: [fig-collections/set_1.png, width=400 frac=1.0] Множество – это неупорядоченная коллекция уникальных элементов <div id=\"collections:sets:fig:1\"></div> -->\n",
|
||
"<!-- begin figure -->\n",
|
||
"<div id=\"collections:sets:fig:1\"></div>\n",
|
||
"\n",
|
||
"<!-- end figure -->\n",
|
||
"\n",
|
||
"\n",
|
||
"Тип данных `set` может вызываться как функция `set()` –\n",
|
||
"без аргументов она возвращает пустое множество; с аргументом типа\n",
|
||
"`set` возвращает поверхностную копию аргумента; в случае, если\n",
|
||
"аргумент имеет другой тип, выполняется попытка преобразовать его в\n",
|
||
"объект типа `set`. Эта функция принимает не более одного\n",
|
||
"аргумента. Кроме того, непустые множества могут создаваться без\n",
|
||
"использования функции `set()`, а пустые множества могут создаваться\n",
|
||
"только с помощью функции `set()` – их нельзя создать с помощью пары\n",
|
||
"пустых скобок. Множество, состоящее из одного или более элементов,\n",
|
||
"может быть создано с помощью последовательности элементов, разделенных\n",
|
||
"запятыми, заключенной в фигурные скобки. Другой способ создания\n",
|
||
"множеств заключается в использовании генераторов множеств. Множества\n",
|
||
"всегда содержат уникальные элементы – добавление повторяющихся\n",
|
||
"элементов возможно, но не имеет смысла. Например, следующие три\n",
|
||
"множества являются эквивалентными: `set(\"apple\")`, `set(\"aple\")` и\n",
|
||
"`{'e', 'p', 'a', 'l'}`. Благодаря этой их особенности множества часто\n",
|
||
"используются для устранения повторяющихся значений. Например, если\n",
|
||
"предположить, что `x` – это список строк, то после выполнения\n",
|
||
"инструкции `x = list(set(x))` в списке останутся только уникальные\n",
|
||
"строки, причем располагаться они могут в произвольном порядке.\n",
|
||
"\n",
|
||
"<!-- dom:FIGURE: [fig-collections/set_2.png, width=800 frac=1.0] Стандартные операторы множеств <div id=\"collections:sets:fig:2\"></div> -->\n",
|
||
"<!-- begin figure -->\n",
|
||
"<div id=\"collections:sets:fig:2\"></div>\n",
|
||
"\n",
|
||
"<!-- end figure -->\n",
|
||
"\n",
|
||
"\n",
|
||
"Множества поддерживают встроенную функцию `len()` и быструю проверку\n",
|
||
"на вхождение с помощью операторов `in` и `not in`. Они также предоставляют\n",
|
||
"типичный набор операторов, как показано на рис. . \n",
|
||
"Полный перечень методов и операторов, применимых к множествам,\n",
|
||
"приводится в табл. [2](#collections:sets:tbl:1). Все методы семейства\n",
|
||
"«update» (`set.update()`, `set.intersection_update()` и т. д.) могут принимать в качестве\n",
|
||
"аргумента любые итерируемые объекты, но эквивалентные им\n",
|
||
"комбинированные операторы присваивания (`|=`, `&=` и т. д.) требуют, чтобы\n",
|
||
"оба операнда были множествами. "
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Таблица 2 : Методы и операторы множеств <div id=\"collections:sets:tbl:1\"></div>\n",
|
||
"\n",
|
||
"\n",
|
||
"<table border=\"1\">\n",
|
||
"<thead>\n",
|
||
"<tr><th align=\"left\"> Синтаксис </th> <th align=\"left\"> Описание </th> </tr>\n",
|
||
"</thead>\n",
|
||
"<tbody>\n",
|
||
"<tr><td align=\"left\"> <code>s.add(x)</code> </td> <td align=\"left\"> Добавляет элементы <code>x</code> во множество <code>s</code>, если они отсутствуют в <code>s</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.clear()</code> </td> <td align=\"left\"> Удаляет все элементы из множества <code>s</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.difference(t)</code>или <code>s-t</code> </td> <td align=\"left\"> Возвращает новое множество включающее элементы множества <code>s</code>, которые отсутствуют в множестве <code>t</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.difference_update(t)</code> или <code>s-=t</code> </td> <td align=\"left\"> Удаляет из множества <code>s</code> все элементы, присутствующие в множестве <code>t</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.discard(x)</code> </td> <td align=\"left\"> Удаляет элемент <code>x</code> из множества <code>s</code>, если он присутствует в множестве <code>s</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.intersection(t)</code> или <code>s & t</code> </td> <td align=\"left\"> Возвращает новое множество, включающее элементы, присутствующие одновременно в множествах <code>s</code> и <code>t</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.intersection_update(t)</code> или <code>s &= t</code> </td> <td align=\"left\"> Оставляет во множестве <code>s</code> пересечение множеств <code>s</code> и <code>t</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.isdisjoint(t)</code> </td> <td align=\"left\"> Возвращает <code>True</code>, если множества <code>s</code> и <code>t</code> не имеют общих элементов </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.issubset(t)</code> или <code>s <= t</code> </td> <td align=\"left\"> Возвращает <code>True</code>, если множество <code>s</code> эквивалентно множеству <code>t</code> или является его подмножеством; чтобы проверить, является ли множество <code>s</code> только подмножеством множества <code>t</code>, следует использовать проверку <code>s < t</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.issuperset(t)</code> или <code>s >= t</code> </td> <td align=\"left\"> Возвращает <code>True</code>, если множество <code>s</code> эквивалентно множеству <code>t</code> или является его надмножеством; чтобы проверить, является ли множество <code>s</code> только надмножеством множества <code>t</code>, следует использовать проверку <code>s > t</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.pop()</code> </td> <td align=\"left\"> Возвращает и удаляет случайный элемент множества <code>s</code> или возбуждает исключение <code>KeyError</code>, если <code>s</code> – это пустое множество </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.remove(x)</code> </td> <td align=\"left\"> Удаляет элемент <code>x</code> из множества <code>s</code> или возбуждает исключение <code>KeyError</code>, если элемент <code>x</code> отсутствует в множестве <code>s</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.symmetric_difference(t)</code> или <code>s ^ t</code> </td> <td align=\"left\"> Возвращает новое множество, включающее все элементы, присутствующие в множествах <code>s</code> и <code>t</code>, за исключением элементов, присутствующих в обоих множествах одновременно </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.symmetric_difference_update(t)</code> или <code>s ^= t</code> </td> <td align=\"left\"> Возвращает в множестве <code>s</code> результат строгой дизъюнкции множеств <code>s</code> и <code>t</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.union(t)</code> или <code>s | t</code> </td> <td align=\"left\"> Возвращает новое множество, включающее все элементы множества <code>s</code> и все элементы множества <code>t</code>, отсутствующие в множестве <code>s</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s.update(t)</code> или <code>s |= t</code> </td> <td align=\"left\"> Добавляет во множество <code>s</code> все элементы множества <code>t</code>, отсутствующие в множестве <code>s</code> </td> </tr>\n",
|
||
"</tbody>\n",
|
||
"</table>"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Генераторы множеств\n",
|
||
"\n",
|
||
"<div id=\"collections:sets:gen\"></div>\n",
|
||
"\n",
|
||
"В дополнение к возможности создавать множества с помощью функции\n",
|
||
"`set()` или литералов, существует возможность создавать множества с\n",
|
||
"помощью *генераторов множеств*. Генератор множества – это выражение и\n",
|
||
"цикл с необязательным условием, заключенные в фигурные скобки. Подобно\n",
|
||
"генераторам списков, генераторы множеств поддерживают две формы\n",
|
||
"записи:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"```Python\n",
|
||
" {expression for item in iterable}\n",
|
||
" {expression for item in iterable if condition}\n",
|
||
"```"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Мы могли бы использовать генераторы множеств для фильтрации\n",
|
||
"нежелательных элементов (когда порядок следования элементов не имеет\n",
|
||
"значения), как показано ниже:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"ename": "NameError",
|
||
"evalue": "name 'files' 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<ipython-input-4-943f481fddb7>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mhtml\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0mx\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mfiles\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mendswith\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\".htm\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\".html\"\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 'files' is not defined"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"html = {x for x in files if x.lower().endswith((\".htm\", \".html\"))}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Если предположить, что `files` – это список имен файлов, то данный\n",
|
||
"генератор множества создает множество `html`, в котором хранятся\n",
|
||
"только имена файлов с расширениями `.htm` и `.html`, независимо от\n",
|
||
"регистра символов.\n",
|
||
"\n",
|
||
"Как и в случае с генераторами списков, в генераторах множеств\n",
|
||
"используются итерируемые объеты, которые в свою очередь могут быть\n",
|
||
"генераторами множеств (или генераторами любого другого типа), что\n",
|
||
"позволяет создавать весьма замысловатые генераторы множеств."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Тип `frozenset`\n",
|
||
"<div id=\"collections:sets:frozenset\"></div>\n",
|
||
"\n",
|
||
"Фиксированное множество (`frozenset`) – это множество, которое после\n",
|
||
"создания невозможно изменить. Хотя при этом мы, конечно, можем\n",
|
||
"повторно связать переменную, которая ссылалась на фиксированное\n",
|
||
"множество, с чем-то другим. Фиксированные множества могут создаваться\n",
|
||
"только в результате обращения к имени типа `frozenset` как к\n",
|
||
"функции. При вызове `frozenset()` без аргументов возвращается пустое\n",
|
||
"фиксированное множество; с аргументом типа `frozenset` возвращается\n",
|
||
"поверхностная копия аргумента; если аргумент имеет другой тип,\n",
|
||
"выполняется попытка преобразовать его в объект типа `frozenset`. Эта\n",
|
||
"функция принимает не более одного аргумента.\n",
|
||
"\n",
|
||
"Поскольку фиксированные множества относятся к категории неизменяемых\n",
|
||
"объектов, они поддерживают только те методы и операторы, которые\n",
|
||
"воспроизводят результат, не оказывая воздействия на фиксированное\n",
|
||
"множество или на множества, к которым они применяются.\n",
|
||
"\n",
|
||
"Поскольку фиксированные множества относятся к категории неизменяемых\n",
|
||
"объектов, они поддерживают только те методы и операторы, которые\n",
|
||
"воспроизводят результат, не оказывая воздействия на фиксированное\n",
|
||
"множество или на множества, к которым они применяются. \n",
|
||
"В табл. [collections:sets:tbl:1](#collections:sets:tbl:1) перечислены все методы множеств из\n",
|
||
"которых фиксированными множествами поддерживаются: `frozenset.copy()`,\n",
|
||
"`frozenset.difference()` (`-`), `frozenset.intersection()` (`&`),\n",
|
||
"`frozenset.isdis-joint()`, `frozenset.issubset()` (`<=` и `<` для\n",
|
||
"выявления подмножеств), `frozenset.issuperset()` (`>=` и `>` для\n",
|
||
"выявления надмножеств), `frozenset.union()` (`|`) и\n",
|
||
"`frozenset.symmetric_difference()` (`^`).\n",
|
||
"\n",
|
||
"Если двухместный оператор применяется ко множеству и фиксированному\n",
|
||
"множеству, тип результата будет совпадать с типом операнда, стоящего\n",
|
||
"слева от оператора. То есть если предположить, что `f` – это\n",
|
||
"фиксированное множество, а `s` – это обычное множество, то выражение\n",
|
||
"`f & s` вернет объект типа frozenset, а выражение `s & f` – объект\n",
|
||
"типа `set`. В случае операторов `==` и `!=` порядок операндов не имеет\n",
|
||
"значения, и выражение `f == s` вернет `True`, только если оба\n",
|
||
"множества содержат одни и те же элементы.\n",
|
||
"\n",
|
||
"Другое следствие неизменности фиксированных множеств заключается в\n",
|
||
"том, что они соответствуют критерию хеширования, предъявляемому к\n",
|
||
"элементам множеств, и потому множества и фиксированные множества могут\n",
|
||
"содержать другие фиксированные множества.\n",
|
||
"\n",
|
||
"\n",
|
||
"<!-- Local Variables: -->\n",
|
||
"<!-- doconce-chapter-nickname: \"collections\" -->\n",
|
||
"<!-- doconce-section-nickname: \"sets\" -->\n",
|
||
"<!-- End: -->"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Отображения\n",
|
||
"<div id=\"collections:maps\"></div>\n",
|
||
"\n",
|
||
"Отображениями называются типы данных, поддерживающие оператор проверки\n",
|
||
"на вхождение (`in`), функцию `len()` и возможность обхода элементов в\n",
|
||
"цикле. Отображения – это коллекции пар элементов «ключ-значение»,\n",
|
||
"которые предоставляют методы доступа к элементам и их ключам и\n",
|
||
"значениям. При выполнении итераций порядок следования элементов\n",
|
||
"отображений может быть произвольным. В языке Python имеется два типа\n",
|
||
"отображений: встроенный тип `dict` и тип `collections.defaultdict`,\n",
|
||
"определяемый в стандартной библиотеке. Мы будем использовать термин\n",
|
||
"словарь для ссылки на любой из этих типов, когда различия между ними\n",
|
||
"не будут иметь никакого значения."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"ename": "ModuleNotFoundError",
|
||
"evalue": "No module named 'collections.defaultdict'",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||
"\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)",
|
||
"\u001b[0;32m<ipython-input-5-d014d61d1d77>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mcollections\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdefaultdict\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
|
||
"\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'collections.defaultdict'"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"import collections.defaultdict"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"В качестве ключей словарей могут использоваться только хешируемые\n",
|
||
"объекты, поэтому в качестве ключей словаря такие неизменяемые типы,\n",
|
||
"как `float`, `frozenset`, `int`, `str` и `tuple`, использовать\n",
|
||
"допускается, а изменяемые типы, такие как `dict`, `list` и `set`, –\n",
|
||
"нет. С другой стороны, каждому ключу соответствует некоторое значение,\n",
|
||
"которое может быть ссылкой на объект любого типа, включая числа,\n",
|
||
"строки, списки, множества, словари, функции и т. д.\n",
|
||
"\n",
|
||
"Словари могут сравниваться с помощью стандартных операторов сравнения\n",
|
||
"(`<`, `<=`, `==`, `!=`, `>=`, `>`), при этом сравнивание производится\n",
|
||
"поэлементно (и рекурсивно, при наличии вложенных элементов, таких как\n",
|
||
"кортежи или словари в словарях). Пожалуй, единственными операторами\n",
|
||
"сравнения, применение которых к словарям имеет смысл, являются\n",
|
||
"операторы `==` и `!=`."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Словари\n",
|
||
"<div id=\"collections:maps:dict\"></div>\n",
|
||
"\n",
|
||
"Тип `dict` – это неупорядоченная коллекция из нуля или более пар\n",
|
||
"«ключ-значение», в которых в качестве ключей могут использоваться\n",
|
||
"ссылки на хешируемые объекты, а в качестве значений – ссылки на\n",
|
||
"объекты любого типа. Словари относятся к категории изменяемых типов,\n",
|
||
"поэтому легко можно добавлять и удалять их элементы, но так как они\n",
|
||
"являются неупорядоченными коллекциями, к ним не применимо понятие\n",
|
||
"индекса и не применима операция извлечения среза.\n",
|
||
"\n",
|
||
"Тип данных `dict` может вызываться как функция `dict()` – без\n",
|
||
"аргументов она возвращает пустой словарь; если в качестве аргумента\n",
|
||
"передается отображение, возвращается словарь, основанный на этом\n",
|
||
"отображении: например, с аргументом типа `dict` возвращается\n",
|
||
"поверхностная копия словаря. Существует возможность передавать в\n",
|
||
"качестве аргумента последовательности, если каждый элемент\n",
|
||
"последовательности в свою очередь является последовательностью из двух\n",
|
||
"объектов, первый из которых используется в качестве ключа, а второй –\n",
|
||
"в качестве значения. Как вариант, для создания словарей, в которых \n",
|
||
"ключи являются допустимыми идентификаторами языка Python, можно\n",
|
||
"использовать именованные аргументы; тогда имена аргументов будут\n",
|
||
"играть роль ключей, а значения аргументов – роль значений\n",
|
||
"ключей. Кроме того, словари могут создаваться с помощью фигурных\n",
|
||
"скобок – пустые скобки `{}` создадут пустой словарь. Непустые фигурные\n",
|
||
"скобки должны содержать один или более элементов, разделенных\n",
|
||
"запятыми, каждый из которых состоит из ключа, символа двоеточия и\n",
|
||
"значения. Еще один способ создания словарей заключается в\n",
|
||
"использовании генераторов словарей – эта тема будет рассматриваться\n",
|
||
"ниже, в соответствующем подразделе.\n",
|
||
"\n",
|
||
"Ниже приводятся несколько способов создания словарей – все они создают\n",
|
||
"один и тот же словарь:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 34,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"d1 = dict({\"id\": 1948, \"name\": \"Washer\", \"size\": 3})\n",
|
||
"d2 = dict(id=1948, name=\"Washer\", size=3)\n",
|
||
"d3 = dict([(\"id\", 1948), (\"name\", \"Washer\"), (\"size\", 3)])\n",
|
||
"d4 = dict(zip((\"id\", \"name\", \"size\"), (1948, \"Washer\", 3)))\n",
|
||
"d5 = {\"id\": 1948, \"name\": \"Washer\", \"size\": 3}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"<!-- dom:FIGURE: [fig-collections/map_1.png, width=800 frac=1.0] Словарь – это неупорядоченная коллекция элементов (ключ, значение) с уникальными ключами <div id=\"collections:maps:fig:1\"></div> -->\n",
|
||
"<!-- begin figure -->\n",
|
||
"<div id=\"collections:maps:fig:1\"></div>\n",
|
||
"<!-- end figure -->\n",
|
||
"\n",
|
||
"\n",
|
||
"На рис. [collections:maps:fig:1](#collections:maps:fig:1) демонстрируется словарь, созданный\n",
|
||
"следующим фрагментом программного кода:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 35,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"d = {\"root\": 18, \"blue\": [75, \"R\", 2], 21: \"venus\", -14: None,\n",
|
||
" \"mars\": \"rover\", (4, 11): 18, 0: 45}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Ключи словарей являются уникальными, поэтому если в словарь\n",
|
||
"добавляется пара «ключ-значение» с ключом, который уже присутствует в\n",
|
||
"словаре, в результате происходит замена значения существующего ключа\n",
|
||
"новым значением. Для доступа к отдельным элементам используются\n",
|
||
"квадратные скобки: например, выражение `d[\"root\"]` вернет `18`,\n",
|
||
"выражение `d[21]` вернет строку `\"venus\"`, а выражение `d[91]` \n",
|
||
"применительно к словарю, изображенному на\n",
|
||
"рис. [collections:maps:fig:1](#collections:maps:fig:1), возбудит исключение `KeyError`. \n",
|
||
"\n",
|
||
"Квадратные скобки могут также использоваться для добавления и удаления\n",
|
||
"элементов словаря. Чтобы добавить новый элемент, используется оператор\n",
|
||
"`=`, например, `d[\"X\"] = 59`. Для удаления элементов используется\n",
|
||
"инструкция `del`, например, инструкция `del d[\"mars\"]` удалит из \n",
|
||
"словаря элемент с ключом `\"mars\"` или возбудит исключение `KeyError`,\n",
|
||
"если элемент с указанным ключом отсутствует в словаре. Кроме того,\n",
|
||
"элементы могут удаляться (и возвращаться вызывающей программе)\n",
|
||
"методом `dict.pop()`.\n",
|
||
"\n",
|
||
"Словари поддерживают встроенную функцию `len()` и для ключей\n",
|
||
"поддерживают возможность быстрой проверки на вхождение с помощью\n",
|
||
"операторов `in` и `not in`. В табл. [collections:maps:tbl:1](#collections:maps:tbl:1)\n",
|
||
"перечислены все методы словарей.\n",
|
||
"\n",
|
||
"Так как словари содержат пары «ключ-значение», у нас может возникнуть\n",
|
||
"потребность обойти в цикле элементы словаря (ключ, значение) по\n",
|
||
"значениям или по ключам. Например, ниже приводятся два эквивалентных\n",
|
||
"способа обхода пар «ключ-значение»:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"for item in d.items():\n",
|
||
" print(item[0], item[1])"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 36,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"for key, value in d.items():\n",
|
||
" print(key, value)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Таблица 3 : Методы словарей <div id=\"collections:maps:tbl:1\"></div>\n",
|
||
"\n",
|
||
"\n",
|
||
"<table border=\"1\">\n",
|
||
"<thead>\n",
|
||
"<tr><th align=\"left\"> Синтаксис </th> <th align=\"left\"> Описание </th> </tr>\n",
|
||
"</thead>\n",
|
||
"<tbody>\n",
|
||
"<tr><td align=\"left\"> <code>d.clear()</code> </td> <td align=\"left\"> Удаляет все элементы из словаря <code>d</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>d.copy()</code> </td> <td align=\"left\"> Возвращает поверхностную копию словаря <code>d</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>d.fromkeys(s, v)</code> </td> <td align=\"left\"> Возвращает словарь типа <code>dict</code>, ключами которого являются элементы последовательности <code>s</code> значениями либо <code>None</code> либо <code>v</code>, если аргумент <code>v</code> определен </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>d.get(k)</code> </td> <td align=\"left\"> Возвращает значение ключа <code>k</code> или <code>None</code>, если ключ <code>k</code> отсутствует в словаре </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>d.get(k, v)</code> </td> <td align=\"left\"> Возвращает значение ключа <code>k</code> или <code>v</code>, если ключ <code>k</code> отсутствует в словаре </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>d.items()</code> </td> <td align=\"left\"> Возвращает представление всех пар (ключ, значение) в словаре <code>d</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>d.keys()</code> </td> <td align=\"left\"> Возвращает представление всех ключей словаря <code>d</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>d.pop(k)</code> </td> <td align=\"left\"> Возвращает значение ключа <code>k</code> и удаляет из словаря элемент с ключом <code>k</code> или возбуждает исключение <code>KeyError</code>, если ключ <code>k</code> отсутствует в словаре </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>d.pop(k, v)</code> </td> <td align=\"left\"> Возвращает значение ключа <code>k</code> и удаляет из словаря элемент с ключом <code>k</code> или возвращает значение <code>v</code>, если ключ <code>k</code> отсутствует в словаре </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>d.popitem()</code> </td> <td align=\"left\"> Возвращает и удаляет произвольную пару (ключ, значение) из словаря <code>d</code> или возбуждает исключение <code>KeyError</code>, если словарь <code>d</code> пуст </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>d.setdefault(k, v)</code> </td> <td align=\"left\"> То же что и <code>dict.get()</code> за исключением того, что, если ключ <code>k</code> в словаре отсутствует, в словарь вставляется новый элемент с ключом <code>k</code> и со значением <code>None</code> или <code>v</code>, если аргумент <code>v</code> задан </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>d.update(a)</code> </td> <td align=\"left\"> Добавляет в словарь <code>d</code> пары (ключ, значение) из <code>a</code>, которые отсутствуют в словаре <code>d</code> а для каждого ключа который уже присутствует в словаре <code>d</code> выполняется замена соответствующим значением из <code>a</code>; a может быть словарем итерируемым объектом с парами (ключ значение) или именованными аргументами </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>d.values()</code> </td> <td align=\"left\"> Возвращает представление всех значений в словаре <code>d</code> </td> </tr>\n",
|
||
"</tbody>\n",
|
||
"</table>\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"Обход значений в словаре выполняется похожим способом:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 37,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"for value in d.values():\n",
|
||
" print(value)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Для обхода ключей в словаре можно использовать метод `dict.keys()`\n",
|
||
"или просто интерпретировать словарь как итерируемый объект и вы-\n",
|
||
"полнить итерации по его ключам, как показано в следующих двух\n",
|
||
"фрагментах:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 38,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"for key in d:\n",
|
||
" print(key)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 39,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"for key in d.keys():\n",
|
||
" print(key)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Если необходимо изменить значения в словаре, то можно выполнить\n",
|
||
"обход ключей словаря в цикле и изменить значения, используя оператор\n",
|
||
"квадратных скобок. Например, ниже показано, как можно было бы\n",
|
||
"увеличить все значения в словаре `d`, если предполагать, что все\n",
|
||
"значения являются числами:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"ename": "NameError",
|
||
"evalue": "name 'd' 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<ipython-input-6-24b8a3baf94f>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mkey\u001b[0m \u001b[0;32min\u001b[0m \u001b[0md\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[0md\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
|
||
"\u001b[0;31mNameError\u001b[0m: name 'd' is not defined"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"for key in d:\n",
|
||
" d[key] += 1"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Методы `dict.items()`, `dict.keys()` и `dict.values()` возвращают\n",
|
||
"представления словарей. Представление словаря – это в действительности\n",
|
||
"итерируемый объект, доступный только для чтения и хранящий элементы,\n",
|
||
"ключи или значения словаря в зависимости от того, какое представление\n",
|
||
"было запрошено."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Генераторы словарей\n",
|
||
"\n",
|
||
"<div id=\"collections:maps:dict:gen\"></div>\n",
|
||
"\n",
|
||
"*Генератор словарей* – это выражение и цикл с необязательным условием,\n",
|
||
"заключенное в фигурные скобки, очень напоминающее генератор\n",
|
||
"множеств. Подобно генераторам списков и множеств, генераторы словарей\n",
|
||
"поддерживают две формы записи:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"```Python\n",
|
||
" {keyexpression: valueexpression for key, value in iterable}\n",
|
||
" {keyexpression: valueexpression for key, value in iterable if condition}\n",
|
||
"```"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Ниже показано, как можно использовать генератор словарей для создания\n",
|
||
"словаря, в котором каждый ключ является именем файла в текущем\n",
|
||
"каталоге, а каждое значение – это размер файла в байтах:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"```Python\n",
|
||
" file_sizes = {name: os.path.getsize(name) for name in os.listdir(\".\")}\n",
|
||
"```"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Функция `os.listdir()` из модуля os («operating system» – операционная\n",
|
||
"система) возвращает список файлов и каталогов в указанном каталоге, но\n",
|
||
"при этом в список никогда не включаются специальные имена каталогов\n",
|
||
"«.» или «..». Функция os.path.getsize() возвращает размер заданного\n",
|
||
"файла в байтах. Чтобы отфильтровать каталоги и другие элементы списка,\n",
|
||
"не являющиеся файлами, можно добавить дополнительное условие:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"```Python\n",
|
||
" file_sizes = {name: os.path.getsize(name) for name in os.listdir(\".\")\n",
|
||
" \t if os.path.isfile(name)}\n",
|
||
"```"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Функция `os.path.isfile()` из модуля `os.path` возвращает `True`, если\n",
|
||
"указанный путь соответствует файлу, и `False` – в противном случае, то\n",
|
||
"есть для каталогов, ссылок и тому подобного.\n",
|
||
"\n",
|
||
"Генераторы словарей могут также использоваться для создания инвер-\n",
|
||
"тированных словарей. Например, пусть имеется словарь `d`, тогда мы\n",
|
||
"можем создать новый словарь, ключами которого будут значения словаря\n",
|
||
"`d`, а значениями – ключи словаря `d`:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"```Python\n",
|
||
" inverted_d = {v: k for k, v in d.items()}\n",
|
||
"```"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Полученный словарь можно инвертировать обратно и получить\n",
|
||
"первоначальный словарь – при условии, что все значения в\n",
|
||
"первоначальном словаре были уникальными, однако инверсия будет терпеть\n",
|
||
"неудачу, с возбуждением исключения `TypeError`, если какое-либо\n",
|
||
"значение окажется не хешируемым."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Словари со значениями по умолчанию\n",
|
||
"<div id=\"collections:maps:defaultdict\"></div>\n",
|
||
"\n",
|
||
"Словари со значениями по умолчанию – это обычные словари, они\n",
|
||
"поддерживают те же самые методы и операторы, что и обычные\n",
|
||
"словари. Единственное, что отличает такие словари от обычных словарей,\n",
|
||
"– это способ обработки отсутствующих ключей, но во всех остальных\n",
|
||
"отношениях они ничем не отличаются друг от друга. \n",
|
||
"\n",
|
||
"При обращении к несуществующему («отсутствующему») ключу слова-\n",
|
||
"ря возбуждается исключение `KeyError`. Это очень удобно, так как\n",
|
||
"нередко для нас бывает желательно знать об отсутствии ключа, который,\n",
|
||
"согласно нашим предположениям, может присутствовать. Но в некоторых\n",
|
||
"случаях бывает необходимо, чтобы в словаре присутствовали все ключи,\n",
|
||
"которые мы используем, даже если это означает, что элемент с заданным\n",
|
||
"ключом добавляется в словарь в момент первого обращения к нему.\n",
|
||
"\n",
|
||
"Например, допустим, что имеется словарь `d`, который не имеет элемента\n",
|
||
"с ключом m, тогда выражение `x = d[m]` возбудит исключение\n",
|
||
"`KeyError`. Если `d` – это словарь со значениями по умолчанию,\n",
|
||
"созданный соответствующим способом, а элемент с ключом m принадлежит\n",
|
||
"такому словарю, то при обращении к нему будет возвращено\n",
|
||
"соответствующее значение, как и в случае с обычным словарем. Но если в\n",
|
||
"словаре со значениями по умолчанию отсутствует ключ m, то будет создан\n",
|
||
"новый элемент словаря с ключом m и со значением по умолчанию, и будет\n",
|
||
"возвращено значение этого, вновь созданного элемента. \n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"<!-- Local Variables: -->\n",
|
||
"<!-- doconce-chapter-nickname: \"collections\" -->\n",
|
||
"<!-- doconce-section-nickname: \"maps\" -->\n",
|
||
"<!-- End: -->"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Обход в цикле и копирование коллекций\n",
|
||
"<div id=\"collections:iterandcopy\"></div>\n",
|
||
"\n",
|
||
"После того как будет создана коллекция элементов данных, вполне\n",
|
||
"естественно возникает желание обойти все элементы, содержащиеся в\n",
|
||
"ней. Еще одна часто выполняемая операция – копирование коллекций. Из-за\n",
|
||
"того, что в языке Python повсеместно используются ссылки на объекты\n",
|
||
"(ради повышения эффективности), существуют некоторые особенности,\n",
|
||
"связанные с копированием.\n",
|
||
"\n",
|
||
"В этом разделе сначала мы рассмотрим итераторы языка Python, а затем\n",
|
||
"принципы копирования коллекций."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Итераторы, функции и операторы для работы с итерируемыми объектами\n",
|
||
"<div id=\"collections:iterandcopy:iterator\"></div>\n",
|
||
"\n",
|
||
"*Итерируемый* тип данных – это такой тип, который может возвращать\n",
|
||
"свои элементы по одному. Любой объект, имеющий метод `__iter__()`, или\n",
|
||
"любая последовательность (то есть объект, имеющий метод\n",
|
||
"`__getitem__()`, принимающий целочисленный аргумент со значением от\n",
|
||
"`0` и выше), является итерируемым и может предоставлять\n",
|
||
"итератор. Итератор – это объект, имеющий метод `__next__()`, который\n",
|
||
"при каждом вызове возвращает очередной элемент и возбуждает исключение\n",
|
||
"`StopIteration` после исчерпания всех элементов. В\n",
|
||
"табл. [4](#collections:iterandcopy:iterator:tbl:1) перечислены\n",
|
||
"операторы и функции, которые могут применяться к итерируемым\n",
|
||
"объектам. "
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Таблица 4 : Общие функции и операторы для работы с итерируемыми объектами <div id=\"collections:iterandcopy:iterator:tbl:1\"></div>\n",
|
||
"\n",
|
||
"<table border=\"1\">\n",
|
||
"<thead>\n",
|
||
"<tr><th align=\"left\"> Синтаксис </th> <th align=\"left\"> Описание </th> </tr>\n",
|
||
"</thead>\n",
|
||
"<tbody>\n",
|
||
"<tr><td align=\"left\"> <code>s + t</code> </td> <td align=\"left\"> Возвращает конкатенацию последовательностей <code>s</code> и <code>t</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>s * n</code> </td> <td align=\"left\"> Возвращает конкатенацию из <code>int n</code> последовательностей <code>s</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>x in i</code> </td> <td align=\"left\"> Возвращает <code>True</code>, если элемент <code>x</code> присутствует в итерируемом объекте <code>i</code>, обратная проверка выполняется с помощью оператора <code>not in</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>all(i)</code> </td> <td align=\"left\"> Возвращает <code>True</code>, если все элементы итерируемого объекта <code>i</code> в логическом контексте оцениваются как значение <code>True</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>any(i)</code> </td> <td align=\"left\"> Возвращает <code>True</code>, если хотя бы один элемент итерируемого объекта <code>i</code> в логическом контексте оценивается как значение <code>True</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>enumerate (i,start)</code> </td> <td align=\"left\"> Обычно используется в циклах <code>for ... in</code>, чтобы получить последовательность кортежей <code>(index, item)</code>, где значения индексов начинают отсчитывать от <code>0</code> или от значения <code>start</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>len(x)</code> </td> <td align=\"left\"> Возвращает «длину» объекта <code>x</code>. Если <code>x</code> – коллекция, то возвращаемое число представляет количество элементов. Если <code>x</code> – строка, то возвращаемое число представляет количество символов </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>max(i, key)</code> </td> <td align=\"left\"> Возвращает наибольший элемент в итерируемом объекте <code>i</code> или элемент с наибольшим значением <code>key(item)</code>, если функция <code>key</code> определена </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>min(i, key)</code> </td> <td align=\"left\"> Возвращает наименьший элемент в итерируемом объекте <code>i</code> или элемент с наименьшим значением <code>key(item)</code>, если функция <code>key</code> определена </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>range(start, stop, step)</code> </td> <td align=\"left\"> Возвращает целочисленный итератор. С одним аргументом (<code>stop</code>) итератор представляет последовательность целых чисел от <code>0</code> до <code>stop-1</code>, с двумя аргументами (<code>start</code>, <code>stop</code>) – последовательность целых чисел от <code>start</code> до <code>stop-1</code>, с тремя аргументами – последовательность целых чисел от <code>start</code> до <code>stop-1</code> c шагом <code>step</code> </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>reversed(i)</code> </td> <td align=\"left\"> Возвращает итератор, который будет возвращать элементы итератора <code>i</code> в обратном порядке </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>sorted(i, key, reverse)</code> </td> <td align=\"left\"> Возвращает список элементов итератора <code>i</code> в отсортированном порядке. Аргумент <code>key</code> используется для выполнения сортировки DSU (Decorate, Sort, Undecorate – декорирование, сортировка, обратное декорирование). Если аргумент <code>reverse</code> имеет значение <code>True</code>, сортировка выполняется в обратном порядке </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>sum(i, start)</code> </td> <td align=\"left\"> Возвращает сумму элементов итерируемого объекта <code>i</code>, плюс ар гумент <code>start</code> (значение которого по умолчанию равно <code>0</code>). Объект <code>i</code> не должен содержать строк </td> </tr>\n",
|
||
"<tr><td align=\"left\"> <code>zip(i1, ..., iN)</code> </td> <td align=\"left\"> Возвращает итератор кортежей, используя итераторы от <code>i1</code> до <code>iN</code> </td> </tr>\n",
|
||
"</tbody>\n",
|
||
"</table>\n",
|
||
"\n",
|
||
"\n",
|
||
"Порядок, в котором возвращаются элементы, зависит от итерируемого\n",
|
||
"объекта. В случае списков и кортежей элементы обычно возвращаются\n",
|
||
"в предопределенном порядке, начиная с первого элемента (находящегося в\n",
|
||
"позиции с индексом `0`), но другие итераторы возвращают элементы в\n",
|
||
"произвольном порядке – например, итераторы словарей и множеств.\n",
|
||
"\n",
|
||
"Встроенная функция `iter()` используется двумя совершенно различными\n",
|
||
"способами. Применяемая к коллекции или к последовательности, она\n",
|
||
"возвращает итератор для заданного объекта или возбуждает исключение\n",
|
||
"`TypeError`, если объект не является итерируемым. Такой способ часто\n",
|
||
"используется при работе с нестандартными типами коллекций \n",
|
||
"и крайне редко – в других контекстах. Во втором варианте использования\n",
|
||
"функции `iter()` ей передается вызываемый объект (функция или метод) и\n",
|
||
"специальное значение. В этом случае полученная функция или метод\n",
|
||
"вызывается на каждой итерации, а значение этой функции, если оно не\n",
|
||
"равно специальному значению, возвращается вызывающей программе; в\n",
|
||
"противном случае возбуждается исключение `StopIteration`.\n",
|
||
"\n",
|
||
"Когда в программе используется цикл `for item in iterable`,\n",
|
||
"интерпретатор Python вызывает функцию `iter(iterable)`, чтобы получить\n",
|
||
"итератор. После этого на каждой итерации вызывается метод `__next__()`\n",
|
||
"итератора, чтобы получить очередной элемент, а когда возбуждается\n",
|
||
"исключение `StopIteration`, оно перехватывается и цикл завершается.\n",
|
||
"Другой способ получить очередной элемент итератора состоит в том,\n",
|
||
"чтобы вызвать встроенную функцию `next()`. Ниже приводятся два\n",
|
||
"эквивалентных фрагмента программного кода (оба они вычисляют\n",
|
||
"произведение элементов списка), в одном из них используется цикл\n",
|
||
"`for ... in`, а во втором явно используется итератор:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 41,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"product = 1\n",
|
||
"for i in [1, 2, 4, 8]:\n",
|
||
" product *= i\n",
|
||
"print(product)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 8,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"64\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"product = 1\n",
|
||
"i = iter([1, 2, 4, 8])\n",
|
||
"while True:\n",
|
||
" try:\n",
|
||
" product *= next(i)\n",
|
||
" except StopIteration:\n",
|
||
" break\n",
|
||
"print(product)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Любой (конечный) итерируемый объект `i` может быть преобразован\n",
|
||
"в кортеж вызовом функции `tuple(i)` или в список – вызовом функции\n",
|
||
"`list(i)`."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Копирование коллекций\n",
|
||
"<div id=\"collections:iterandcopy:copy\"></div>\n",
|
||
"\n",
|
||
"Поскольку в языке Python повсюду используются ссылки на объекты, когда\n",
|
||
"выполняется оператор присваивания (`=`), никакого копирования данных\n",
|
||
"на самом деле не происходит. Если справа от оператора находится\n",
|
||
"литерал, например, строка или число, в операнд слева записывается\n",
|
||
"ссылка, которая указывает на объект в памяти, хранящий значение\n",
|
||
"литерала. Если справа находится ссылка на объект, в левый операнд\n",
|
||
"записывается ссылка, указывающая на тот же самый объект, на который\n",
|
||
"ссылается правый операнд. Вследствие этого операция присваивания\n",
|
||
"обладает чрезвычайно высокой скоростью выполнения. \n",
|
||
"\n",
|
||
"Когда выполняется присваивание крупной коллекции, такой как\n",
|
||
"длинный список, экономия времени становится более чем очевидной.\n",
|
||
"Например:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 43,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"songs = [\"Because\", \"Boys\", \"Carol\"]\n",
|
||
"beatles = songs\n",
|
||
"beatles, songs"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Здесь была создана новая ссылка на объект (`beatles`), и обе ссылки\n",
|
||
"указывают на один и тот же список – никакого копирования данных не\n",
|
||
"производилось. \n",
|
||
"\n",
|
||
"Поскольку списки относятся к категории изменяемых объектов, мы\n",
|
||
"можем вносить в них изменения. Например:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 44,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"beatles[2] = \"Cayenne\"\n",
|
||
"beatles, songs"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Изменения были внесены с использованием переменной `beatles`, но это\n",
|
||
"всего лишь ссылка, указывающая на тот же самый объект, что и ссылка\n",
|
||
"songs. Поэтому любые изменения, произведенные с использованием одной\n",
|
||
"ссылки, можно наблюдать с использованием другой ссылки. \n",
|
||
"Часто это именно то, что нам требуется, поскольку копирование крупных\n",
|
||
"коллекций может оказаться дорогостоящей операцией. Кроме того, это\n",
|
||
"также означает, что имеется возможность передавать списки или другие\n",
|
||
"изменяемые коллекции в виде аргументов функций, изменять эти коллекции\n",
|
||
"в функциях и пребывать в уверенности, что изменения будут доступны\n",
|
||
"после того, как функция вернет управление вызывающей программе. \n",
|
||
"\n",
|
||
"Однако в некоторых ситуациях действительно бывает необходимо создать\n",
|
||
"отдельную копию коллекции (то есть создать другой изменяемый\n",
|
||
"объект). В случае последовательностей, когда выполняется оператор\n",
|
||
"извлечения среза, например, `songs[:2]`, полученный срез – это всегда \n",
|
||
"независимая копия элементов. Поэтому скопировать последовательность\n",
|
||
"целиком можно следующим способом:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 45,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"songs = [\"Because\", \"Boys\", \"Carol\"]\n",
|
||
"beatles = songs[:]\n",
|
||
"beatles[2] = \"Cayenne\"\n",
|
||
"beatles, songs"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"В случае словарей и множеств копирование можно выполнить с помощью\n",
|
||
"методов `dict.copy()` и `set.copy()`. Кроме того, в модуле copy\n",
|
||
"имеется функция copy.copy(), которая возвращает копию заданного\n",
|
||
"объекта. Другой способ копирования встроенных типов коллекций\n",
|
||
"заключается в использовании имени типа как функции, которой в качестве\n",
|
||
"аргумента передается копируемая коллекция. Например:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 46,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"copy_of_dict_d = dict(d)\n",
|
||
"copy_of_list_L = list(L)\n",
|
||
"copy_of_set_s = set(s)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Обратите внимание, что все эти приемы копирования\n",
|
||
"создают *поверхностные копии*, то есть копируются только ссылки на\n",
|
||
"объекты, но не сами объекты. Для неизменяемых типов данных, таких как числа\n",
|
||
"и строки, это равносильно копированию (за исключением более высокой\n",
|
||
"эффективности), но для изменяемых типов данных, таких как вложенные\n",
|
||
"коллекции, это означает, что ссылки в оригинальной коллекции и в копии\n",
|
||
"будут указывать на одни и те же объекты. Эту особенность иллюстрирует\n",
|
||
"следующий пример:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 47,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"x = [53, 68, [\"A\", \"B\", \"C\"]]\n",
|
||
"y = x[:] # поверхностное копирование\n",
|
||
"x, y"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 48,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"y[1] = 40\n",
|
||
"x[2][0] = 'Q'\n",
|
||
"x, y"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Когда выполняется поверхностное копирование списка `x`, копируется\n",
|
||
"ссылка на вложенный список `[\"A\", \"B\", \"C\"]`. Это означает, что третий\n",
|
||
"элемент в обоих списках, `x` и `y`, ссылается на один и тот же список,\n",
|
||
"поэтому любые изменения, произведенные во вложенном списке, можно\n",
|
||
"наблюдать с помощью любой из ссылок, `x` или `y`. Если действительно\n",
|
||
"необходимо создать абсолютно независимую копию коллекции с\n",
|
||
"произвольной глубиной вложенности, необходимо выполнить глубокое \n",
|
||
"копирование:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"([53, 68, ['Q', 'B', 'C']], [53, 40, ['A', 'B', 'C']])"
|
||
]
|
||
},
|
||
"execution_count": 9,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"import copy\n",
|
||
"x = [53, 68, [\"A\", \"B\", \"C\"]]\n",
|
||
"y = copy.deepcopy(x)\n",
|
||
"y[1] = 40\n",
|
||
"x[2][0] = 'Q'\n",
|
||
"x, y"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Здесь списки `x` и `y`, а также элементы, которые они содержат,\n",
|
||
"полностью независимы. "
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"<!-- Local Variables: -->\n",
|
||
"<!-- doconce-chapter-nickname: \"collections\" -->\n",
|
||
"<!-- doconce-section-nickname: \"iterandcopy\" -->\n",
|
||
"<!-- End: -->\n",
|
||
"# Примеры\n",
|
||
"<div id=\"collections:examples\"></div>\n",
|
||
"\n",
|
||
"Сейчас рассмотрим пару примеров, в которых используется многое из\n",
|
||
"того, о чем рассказывалось в этой и в предыдущей главах.\n",
|
||
"\n",
|
||
"Первая программа имеет отношение к обработке текстовой\n",
|
||
"информации. Вторая программа содержит примерно девяносто строк и\n",
|
||
"предназначена для выполнения математических вычислений. Обе программы\n",
|
||
"используют словари, списки, именованные кортежи и множества и обе\n",
|
||
"широко используют метод `str.format()`, который был описан в предыдущей\n",
|
||
"главе."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Генератор имен пользователей\n",
|
||
"<div id=\"collections:examples:gu\"></div>\n",
|
||
"\n",
|
||
"Представьте, что мы выполняем настройку новой компьютерной системы и\n",
|
||
"нам необходимо сгенерировать имена пользователей для всех служащих\n",
|
||
"нашей компании. У нас имеется простой текстовый файл (в кодировке\n",
|
||
"UTF-8), где каждая строка представляет собой запись из полей,\n",
|
||
"разделенных двоеточиями. Каждая запись соответствует одному сотруднику\n",
|
||
"компании и содержит следующие поля: уникальный идентификатор\n",
|
||
"служащего, имя, отчество (это поле может быть пустым), фамилию и\n",
|
||
"название отдела.\n",
|
||
"\n",
|
||
"Ниже в качестве примера приводятся несколько строк из файла\n",
|
||
"[users.txt](src-collections/users.txt) :"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
" 1080:Priscillia:Forbes:Shepard:Cleaning Services\n",
|
||
" 4382:Devan::Fielder:Public Relations\n",
|
||
" 6285:Grey::Collyer:Public Relations\n",
|
||
" 6201:Kierah::Battaile:Catering\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Программа должна читать данные из файла, который указан в командной\n",
|
||
"строке, извлекать отдельные поля из каждой строки (записи) и\n",
|
||
"возвращать подходящие имена пользователей. Каждое имя пользователя\n",
|
||
"должно быть уникальным и должно создаваться на основе имени\n",
|
||
"сотрудника. Результаты должны выводиться на консоль в текстовом виде,\n",
|
||
"отсортированными в алфавитном порядке по фамилии и имени, например:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
" Terminal> ./generate_usernames.py users.txt\n",
|
||
" Name ID Username \n",
|
||
" -------------------------------- ------ ---------\n",
|
||
" Battaile, Kierah................ (6201) kbattail \n",
|
||
" Blakey, Kelci-Louise............ (0641) kblakey \n",
|
||
" Blenkinsop, Keirien............. (4541) kblenkin \n",
|
||
" Clifford, Rebbekkah............. (6263) rcliffor \n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Каждая запись имеет точно пять полей, и хотя можно обращаться\n",
|
||
"к ним по числовым индексам, тем не менее мы будем использовать\n",
|
||
"осмысленные имена, чтобы сделать программный код более понятным:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 50,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"ID, FORENAME, MIDDLENAME, SURNAME, DEPARTMENT = range(5)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"В языке Python общепринято использовать только символы верхнего\n",
|
||
"регистра для идентификаторов, которые будут играть роль констант.\n",
|
||
"\n",
|
||
"Нам также необходимо создать тип именованного кортежа, где будут\n",
|
||
"храниться данные о текущем пользователе:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 51,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"\n",
|
||
"User = collections.namedtuple(\"User\",\n",
|
||
" \"username forename middlename surname id\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Основная логика программы сосредоточена в функции `main()`:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 52,
|
||
"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} file1 [file2 [... fileN]]\".format(\n",
|
||
" sys.argv[0]))\n",
|
||
" sys.exit()\n",
|
||
"\n",
|
||
" usernames = set()\n",
|
||
" users = {}\n",
|
||
" for filename in sys.argv[1:]:\n",
|
||
" with open(filename, encoding=\"utf8\") as file:\n",
|
||
" for line in file:\n",
|
||
" line = line.rstrip()\n",
|
||
" if line:\n",
|
||
" user = process_line(line, usernames)\n",
|
||
" users[(user.surname.lower(), user.forename.lower(),\n",
|
||
" user.id)] = user\n",
|
||
" print_users(users)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Если пользователь не ввел в командной строке имя какого-нибудь\n",
|
||
"файла или ввел параметр `-h` или `--help`, то программа просто выводит\n",
|
||
"текст сообщения с инструкцией о порядке использования и завершает\n",
|
||
"работу.\n",
|
||
"\n",
|
||
"Из каждой прочитанной строки удаляются любые завершающие пробельные\n",
|
||
"символы (такие как `\\n`), и обработка строки продолжается, только если\n",
|
||
"она не пустая. Это означает, что если в данных содержатся пустые\n",
|
||
"строки, они будут просто проигнорированы.\n",
|
||
"\n",
|
||
"Все сгенерированные имена пользователей сохраняются в множестве\n",
|
||
"`usernames`, чтобы гарантировать отсутствие повторяющихся имен\n",
|
||
"пользователей. Сами данные сохраняются в словаре `users`. Информация о\n",
|
||
"каждом пользователе сохраняется в виде элемента словаря, ключом\n",
|
||
"которого является кортеж, содержащий фамилию сотрудника, его имя и\n",
|
||
"идентификатор, а значением – именованный кортеж типа\n",
|
||
"`User`. Использование кортежа, содержащего фамилию сотрудника, его имя \n",
|
||
"и идентификатор, в качестве ключа обеспечивает возможность вызывать\n",
|
||
"функцию `sorted()` для словаря и получать итерируемый объект, в\n",
|
||
"котором элементы будут упорядочены в требуемом нам порядке (то есть\n",
|
||
"фамилия, имя, идентификатор), избежав необходимости создавать функцию,\n",
|
||
"которую пришлось бы передавать в качестве аргумента `key`."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 53,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def process_line(line, usernames):\n",
|
||
" fields = line.split(\":\")\n",
|
||
" username = generate_username(fields, usernames)\n",
|
||
" user = User(username, fields[FORENAME], fields[MIDDLENAME],\n",
|
||
" fields[SURNAME], fields[ID])\n",
|
||
" return user"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Поскольку все записи имеют очень простой формат, и мы уже удалили\n",
|
||
"из строки завершающие пробельные символы, извлечь отдельные поля можно\n",
|
||
"простой разбивкой строки по двоеточиям. Мы передаем список полей и\n",
|
||
"множество `usernames` в функцию `generate_username()` и затем \n",
|
||
"создаем экземпляр именованного кортежа User, который возвращается\n",
|
||
"вызывающей программе (функции `main()`), которая в свою очередь\n",
|
||
"вставляет информацию о пользователе в словарь `users`, готовый для\n",
|
||
"вывода на экран.\n",
|
||
"\n",
|
||
"Если бы мы не создали соответствующие константы для хранения индексов,\n",
|
||
"мы могли бы использовать числовые индексы, как показано ниже:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 54,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"user = User(username, fields[1], fields[2], fields[3], fields[0])"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Хотя такой программный код занимает меньше места, тем не менее\n",
|
||
"это не самое лучшее решение. Во-первых, человеку, который будет\n",
|
||
"сопровождать такой программный код, непонятно, какое поле какую\n",
|
||
"информацию содержит, а, во-вторых, такой программный код чувствителен\n",
|
||
"к изменениям в формате файла с данными – если изменится \n",
|
||
"порядок или число полей в записи, этот программный код окажется\n",
|
||
"неработоспособен. При использовании констант в случае изменения\n",
|
||
"структуры записи нам достаточно будет изменить только значения\n",
|
||
"констант, и программа сохранит свою работоспособность."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 55,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def generate_username(fields, usernames):\n",
|
||
" username = ((fields[FORENAME][0] + fields[MIDDLENAME][:1] +\n",
|
||
" fields[SURNAME]).replace(\"-\", \"\").replace(\"'\", \"\"))\n",
|
||
" username = original_name = username[:8].lower()\n",
|
||
" count = 1\n",
|
||
" while username in usernames:\n",
|
||
" username = \"{0}{1}\".format(original_name, count)\n",
|
||
" count += 1\n",
|
||
" usernames.add(username)\n",
|
||
" return username"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"При первой попытке имя пользователя создается путем конкатенации\n",
|
||
"первого символа имени, первого символа отчества и фамилии целиком,\n",
|
||
"после чего из полученной строки удаляются дефисы и апострофы. \n",
|
||
"Выражение, извлекающее первый символ отчества, таит в себе одну\n",
|
||
"хитрость. Если просто использовать обращение `fields[MIDDLENAME][0]`,\n",
|
||
"то в случае отсутствия отчества будет возбуждено исключение\n",
|
||
"`IndexError`. Но при использовании операции извлечения среза мы\n",
|
||
"получаем либо первый символ отчества, либо пустую строку.\n",
|
||
"\n",
|
||
"Затем мы переводим все символы полученного имени пользователя\n",
|
||
"в нижний регистр и ограничиваем его длину восемью символами. Если\n",
|
||
"имя пользователя уже занято (то есть оно уже присутствует в множестве\n",
|
||
"`usernames`), предпринимается попытка добавить в конец имени\n",
|
||
"пользователя символ `\"1\"`, если это имя пользователя тоже занято, тогда\n",
|
||
"предпринимается попытка добавить символ `\"2\"` и т. д., пока не будет\n",
|
||
"получено незанятое имя пользователя. После этого имя пользователя\n",
|
||
"добавляется в множество `usernames` и возвращается вызывающей\n",
|
||
"программе."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 10,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def print_users(users):\n",
|
||
" namewidth = 32\n",
|
||
" usernamewidth = 9\n",
|
||
"\n",
|
||
" print(\"{0:<{nw}} {1:^6} {2:{uw}}\".format(\n",
|
||
" \"Name\", \"ID\", \"Username\", nw=namewidth, uw=usernamewidth))\n",
|
||
" print(\"{0:-<{nw}} {0:-<6} {0:-<{uw}}\".format(\n",
|
||
" \"\", nw=namewidth, uw=usernamewidth))\n",
|
||
"\n",
|
||
" for key in sorted(users):\n",
|
||
" user = users[key]\n",
|
||
" initial = \"\"\n",
|
||
" if user.middlename:\n",
|
||
" initial = \" \" + user.middlename[0]\n",
|
||
" name = \"{0.surname}, {0.forename}{1}\".format(user, initial)\n",
|
||
" print(\"{0:.<{nw}} ({1.id:4}) {1.username:{uw}}\".format(\n",
|
||
" name, user, nw=namewidth, uw=usernamewidth))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"После обработки всех записей вызывается функция `print_users()`,\n",
|
||
"которой в качестве параметра передается словарь `users`.\n",
|
||
"\n",
|
||
"Первая инструкция `print()` выводит заголовки столбцов. Вторая\n",
|
||
"инструкция `print()` выводит дефисы под каждым из заголовков. В этой\n",
|
||
"второй инструкции метод `str.format()` используется довольно\n",
|
||
"оригинальным образом. Для вывода ему определяется строка `\"\"`, то есть\n",
|
||
"пустая строка; в результате при выводе пустой строки мы получаем\n",
|
||
"строку из дефисов заданной ширины поля вывода.\n",
|
||
"\n",
|
||
"Затем мы используем цикл `for ... in` для вывода информации о каждом\n",
|
||
"пользователе, извлекая ключи из отсортированного словаря. Для удобства\n",
|
||
"мы создаем переменную user, чтобы не вводить каждый раз `users[key]` в\n",
|
||
"оставшейся части функции. В цикле сначала вызывается метод\n",
|
||
"`str.format()`, чтобы записать в переменную name фамилию сотрудника, имя\n",
|
||
"и необязательный первый символ отчества. Обращение к элементам в\n",
|
||
"именованном кортеже user производится по их именам. Собрав строку с\n",
|
||
"именем пользователя, мы выводим информацию о пользователе, ограничивая\n",
|
||
"ширину каждого столбца (имя сотрудника, идентификатор и имя\n",
|
||
"пользователя) желаемыми значениями.\n",
|
||
"\n",
|
||
"Полный программный код программы находится в файле\n",
|
||
"[generate_usernames.py](src-collections/generate_usernames.py)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Статистика\n",
|
||
"<div id=\"collections:examples:stat\"></div>\n",
|
||
"\n",
|
||
"Предположим, что у нас имеется пакет файлов с данными, содержащих\n",
|
||
"числовые результаты некоторой обработки, выполненной нами, и нам\n",
|
||
"необходимо вычислить некоторые основные статистические характеристики,\n",
|
||
"которые дадут нам возможность составить общую картину о полученных\n",
|
||
"данных. В каждом файле находится обычный текст (в кодировке ASCII), с\n",
|
||
"одним или более числами в каждой строке (разделенными пробельными\n",
|
||
"символами).\n",
|
||
"\n",
|
||
"Ниже приводится пример информации, которую нам необходимо получить:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
" Terminal> python statistics.py statistics.dat \n",
|
||
" count = 129\n",
|
||
" mean = 170.49\n",
|
||
" median = 51.00\n",
|
||
" mode = 50.00\n",
|
||
" std. dev. = 269.50\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Здесь видно, что прочитано $129$ чисел, из которых наиболее часто\n",
|
||
"встречается число $50$ со стандартным отклонением по выборке $269.50$.\n",
|
||
"\n",
|
||
"Сами статистические ххарактеристики хранятся в именованном кортеже\n",
|
||
"`Statistics`:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 57,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"Statistics = collections.namedtuple(\"Statistics\",\n",
|
||
" \"mean mode median std_dev\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Функция `main()` может служить схематическим отображением структуры\n",
|
||
"программы:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 58,
|
||
"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} file1 [file2 [... fileN]]\".format(\n",
|
||
" sys.argv[0]))\n",
|
||
" sys.exit()\n",
|
||
"\n",
|
||
" numbers = []\n",
|
||
" frequencies = collections.defaultdict(int)\n",
|
||
" for filename in sys.argv[1:]:\n",
|
||
" read_data(filename, numbers, frequencies)\n",
|
||
" if numbers:\n",
|
||
" statistics = calculate_statistics(numbers, frequencies)\n",
|
||
" print_results(len(numbers), statistics)\n",
|
||
" else:\n",
|
||
" print(\"no numbers found\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Все числа из всех файлов сохраняются в списке `numbers`. Для\n",
|
||
"нахождения модальных («наиболее часто встречающихся») значений нам\n",
|
||
"необходимо знать, сколько раз встречается каждое число, поэтому мы\n",
|
||
"создаем словарь со значениями по умолчанию, используя функцию `int()` \n",
|
||
"в качестве фабричной функции, где будут накапливаться счетчики.\n",
|
||
"\n",
|
||
"Затем выполняется обход списка файлов и производится чтение данных из\n",
|
||
"них. В качестве дополнительных аргументов мы передаем функции\n",
|
||
"`read_data()` список и словарь со значениями по умолчанию, чтобы она\n",
|
||
"могла обновлять их. После чтения всех данных мы исходим из\n",
|
||
"предположения, что некоторые числа были благополучно прочитаны, и\n",
|
||
"вызываем функцию `calculate_statistics()`. Она возвращает именованный\n",
|
||
"кортеж типа `Statistics`, который затем используется для вывода\n",
|
||
"результатов."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 59,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def read_data(filename, numbers, frequencies):\n",
|
||
" with open(filename, encoding=\"ascii\") as file:\n",
|
||
" for lino, line in enumerate(file, start=1):\n",
|
||
" for x in line.split():\n",
|
||
" try:\n",
|
||
" number = float(x)\n",
|
||
" numbers.append(number)\n",
|
||
" frequencies[number] += 1\n",
|
||
" except ValueError as err:\n",
|
||
" print(\"{filename}:{lino}: skipping {x}: {err}\".format(\n",
|
||
" **locals()))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Каждая строка разбивается по пробельным символам, после чего\n",
|
||
"производится попытка преобразовать каждый элемент в число типа\n",
|
||
"`float`. Если преобразование удалось, следовательно, это либо целое\n",
|
||
"число, либо число с плавающей точкой в десятичной или в\n",
|
||
"экспоненциальной форме. Полученное число добавляется в список numbers\n",
|
||
"и выполняется обновление словаря `frequencies` со значениями по\n",
|
||
"умолчанию. (Если бы здесь использовался обычный словарь `dict`,\n",
|
||
"программный код, выполняющий обновление словаря, мог бы выглядеть так:\n",
|
||
"`frequencies[number] = frequencies.get(number, 0) + 1`.) Если\n",
|
||
"преобразование потерпело неудачу, выводится номер строки (счет строк в\n",
|
||
"текстовых файлах по традиции начинается с 1), текст, который программа\n",
|
||
"пыталась преобразовать в число, и сообщение об ошибке, соответствующее\n",
|
||
"исключению `ValueError`."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 60,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def calculate_statistics(numbers, frequencies):\n",
|
||
" mean = sum(numbers) / len(numbers)\n",
|
||
" mode = calculate_mode(frequencies, 3)\n",
|
||
" median = calculate_median(numbers)\n",
|
||
" std_dev = calculate_std_dev(numbers, mean)\n",
|
||
" return Statistics(mean, mode, median, std_dev)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Эта функция используется для сбора всех статистических характеристик\n",
|
||
"воедино. Поскольку среднее значение вычисляется очень просто, мы\n",
|
||
"делаем это прямо здесь. Вычислением других статистических\n",
|
||
"характеристик занимаются отдельные функции, и в заключение данная\n",
|
||
"функция возвращает экземпляр именованного кортежа `Statistics`,\n",
|
||
"содержащий четыре вычисленные статистические характеристики."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 61,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def calculate_mode(frequencies, maximum_modes):\n",
|
||
" highest_frequency = max(frequencies.values())\n",
|
||
" mode = [number for number, frequency in frequencies.items()\n",
|
||
" if frequency == highest_frequency]\n",
|
||
" if not (1 <= len(mode) <= maximum_modes):\n",
|
||
" mode = None\n",
|
||
" else:\n",
|
||
" mode.sort()\n",
|
||
" return mode"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"В выборке может существовать сразу несколько значений, встречающихся\n",
|
||
"наиболее часто, поэтому, помимо словаря `frequencies`, функции\n",
|
||
"передается максимально допустимое число модальных значений. (Эта\n",
|
||
"функция вызывается из `calculate_statistics()`, и при вызове задается\n",
|
||
"максимальное число модальных значений, равное трем.)\n",
|
||
"\n",
|
||
"Функция `max()` используется для поиска наибольшего значения в словаре\n",
|
||
"`frequencies`. Затем с помощью генератора списков создается список из\n",
|
||
"значений, которые равны наивысшему значению. Поскольку числа могут\n",
|
||
"быть с плавающей точкой, мы сравниваем абсолютное значение разницы\n",
|
||
"(используя функцию `math.fabs()`, поскольку она лучше подходит для\n",
|
||
"случаев сравнения малых величин, близких к порогу точности\n",
|
||
"представления числовых значений в компьютере, чем `abs()`) с\n",
|
||
"наименьшим значением, которое может быть представлено компьютером.\n",
|
||
"\n",
|
||
"Если число модальных значений равно 0 или больше максимального,\n",
|
||
"то в качестве модального значения возвращается `None`; в противном \n",
|
||
"случае возвращается сортированный список модальных значений."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 62,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def calculate_median(numbers):\n",
|
||
" numbers = sorted(numbers)\n",
|
||
" middle = len(numbers) // 2\n",
|
||
" median = numbers[middle]\n",
|
||
" if len(numbers) % 2 == 0:\n",
|
||
" median = (median + numbers[middle - 1]) / 2\n",
|
||
" return median"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"*Медиана* («среднее значение») – это значение, находящееся в середине упорядоченной\n",
|
||
"выборки чисел, за исключением случая, когда в выборке присутствует\n",
|
||
"четное число чисел, – тогда значение медианы определяется как среднее\n",
|
||
"арифметическое значение двух чисел, находящихся в середине. \n",
|
||
"\n",
|
||
"Функция вычисления медианы сначала выполняет сортировку чисел\n",
|
||
"по возрастанию. Затем посредством целочисленного деления определяется\n",
|
||
"позиция середины выборки, откуда извлекается число и сохраняется как\n",
|
||
"значение медианы. Если выборка содержит четное число значений, то\n",
|
||
"значение медианы определяется как среднее арифметическое двух чисел,\n",
|
||
"находящихся в середине."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 63,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def calculate_std_dev(numbers, mean):\n",
|
||
" total = 0\n",
|
||
" for number in numbers:\n",
|
||
" total += ((number - mean) ** 2)\n",
|
||
" variance = total / (len(numbers) - 1)\n",
|
||
" return math.sqrt(variance)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"*Стандартное отклонение* – это мера дисперсии; оно определяет, как \n",
|
||
"сильно отклоняются значения в выборке от среднего значения. Вычисление\n",
|
||
"стандартного отклонения в этой функции выполняется по формуле"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"$$\n",
|
||
"\\frac{\\sqrt{\\sum(x + \\bar{x})^2}}{n-1},\n",
|
||
"$$"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"где $x$ — очередное число, $\\bar{x}$ — среднее значение, а $n$ —\n",
|
||
"количество чисел."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 11,
|
||
"metadata": {
|
||
"collapsed": false,
|
||
"jupyter": {
|
||
"outputs_hidden": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def print_results(count, statistics):\n",
|
||
" real = \"9.2f\"\n",
|
||
"\n",
|
||
" if statistics.mode is None:\n",
|
||
" modeline = \"\"\n",
|
||
" elif len(statistics.mode) == 1:\n",
|
||
" modeline = \"mode = {0:{fmt}}\\n\".format(statistics.mode[0], fmt=real)\n",
|
||
" else:\n",
|
||
" modeline = (\"mode = [\" + \", \".join([\"{0:.2f}\".format(m)\n",
|
||
" for m in statistics.mode]) + \"]\\n\")\n",
|
||
"\n",
|
||
" print(\"\"\"\\\n",
|
||
"count = {0:6}\n",
|
||
"mean = {mean:{fmt}}\n",
|
||
"median = {median:{fmt}}\n",
|
||
"{1}\\\n",
|
||
"std. dev. = {std_dev:{fmt}}\"\"\".format(count, modeline, fmt=real, **statistics._asdict()))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Большая часть этой функции связана с форматированием списка модальных\n",
|
||
"значений в строку `modeline`. Если модальные значения отсутствуют, то\n",
|
||
"строка `modeline` вообще не выводится. Если модальное значение\n",
|
||
"единственное, список модальных значений содержит единственный элемент\n",
|
||
"(`mode[0]`), который и выводится с той же строкой форматирования, что\n",
|
||
"используется при выводе других статистических значений. Если имеется\n",
|
||
"несколько модальных значений, они выводятся как список, в котором\n",
|
||
"каждое значение форматируется отдельно. Делается это с помощью\n",
|
||
"генератора списков, который позволяет получить список строк с\n",
|
||
"модальными значениями; строки затем объединяются в единую строку, где\n",
|
||
"отделяются друг от друга запятой с пробелом (`\", \"`). Последняя\n",
|
||
"инструкция `print()` в самом конце получилась очень простой благодаря\n",
|
||
"использованию именованного кортежа. Он позволяет обращаться к\n",
|
||
"статистическим значениям в объекте `statistics`, используя не числовые\n",
|
||
"индексы, а их имена, а благодаря строкам в тройных кавычках мы смогли\n",
|
||
"отформатировать выводимый текст наглядным способом.\n",
|
||
"\n",
|
||
"В этой функции имеется одна особенность, о которой следует упомянуть\n",
|
||
"отдельно. Строка с модальными значениями выводится с помощью элемента\n",
|
||
"строки формата `{2}`, за которым следует символ обратного\n",
|
||
"слеша. Символ обратного слеша экранирует символ перевода строки,\n",
|
||
"поэтому если строка с модальными значениями пустая, то пустая строка\n",
|
||
"выводиться не будет. Именно по этой причине мы вынуждены были добавить\n",
|
||
"символ `\\n` в конец строки `modeline`, если она не пустая.\n",
|
||
"\n",
|
||
"\n",
|
||
"<!-- Local Variables: -->\n",
|
||
"<!-- doconce-chapter-nickname: \"collections\" -->\n",
|
||
"<!-- doconce-section-nickname: \"examples\" -->\n",
|
||
"<!-- End: -->"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Упражнения\n",
|
||
"<div id=\"collections:exercises\"></div>"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"<!-- --- begin exercise --- -->\n",
|
||
"\n",
|
||
"## Модификация вывода в генераторе имен пользователей\n",
|
||
"<div id=\"collections:exercises:\"></div>\n",
|
||
"\n",
|
||
"Модифицируйте программу `generate_usernames.py` так, чтобы в каждой\n",
|
||
"строке она выводила информацию о двух пользователях, ограничив длину\n",
|
||
"имени 17 символами; через каждые 64 строки программа должна выводить\n",
|
||
"символ перевода формата и в начале каждой страницы она должна выводить\n",
|
||
"заголовки столбцов. Ниже приводится пример того, как должен выглядеть\n",
|
||
"вывод программы:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
" Name ID Username Name ID Username \n",
|
||
" ----------------- ------ --------- ----------------- ------ ---------\n",
|
||
" Warne, Brogan A.. (8985) bawarne Wasling, Elisabet (1853) ewasling \n",
|
||
" Webber, Gretchen. (6427) gwebber Weidenmeyer, Deva (6290) dweidenm \n",
|
||
" Wennerbom, Emma.. (8115) ewennerb Wennerbom, Keigan (3617) kywenner\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Достаточно большой объем исходных данных вы найдете в файле\n",
|
||
"[users2.txt](src-collections/users2.txt).\n",
|
||
"\n",
|
||
"<!-- --- begin hint in exercise --- -->\n",
|
||
"\n",
|
||
"**Подсказка.**\n",
|
||
"Это достаточно сложно. Вам потребуется сохранить заголовки\n",
|
||
"столбцов в переменных, чтобы потом их можно было использовать\n",
|
||
"по мере необходимости, и изменить спецификаторы формата, чтобы\n",
|
||
"обеспечить вывод более коротких имен. Один из способов обеспечить\n",
|
||
"постраничный вывод заключается в том, чтобы сохранить все выводимые\n",
|
||
"строки в списке, а затем выполнить обход списка, используя оператор\n",
|
||
"извлечения среза с шагом для получения элементов слева и справа и\n",
|
||
"применяя функцию `zip()` для их объединения.\n",
|
||
"\n",
|
||
"<!-- --- end hint in exercise --- -->\n",
|
||
"Имя файла: `generate_usernames_ans.py`.\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"<!-- Local Variables: -->\n",
|
||
"<!-- doconce-chapter-nickname: \"collections\" -->\n",
|
||
"<!-- doconce-section-nickname: \"exercises\" -->\n",
|
||
"<!-- End: -->\n",
|
||
"\n",
|
||
"\n",
|
||
"<!-- Local Variables: -->\n",
|
||
"<!-- doconce-chapter-nickname: \"collections\" -->\n",
|
||
"<!-- End: -->\n",
|
||
"<!-- --- end exercise --- -->"
|
||
]
|
||
}
|
||
],
|
||
"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
|
||
}
|