{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "# Типы коллекций\n", "\n", " \n", "**С.В. Лемешевский** (email: `sergey.lemeshevsky@gmail.com`), Институт математики НАН Беларуси\n", "\n", "Date: **Mar 18, 2020**\n", "\n", "\n", "\n", "\n", "\n", "\n", "Рассматриваются кортежи и списки, а также новые типы коллекций, включая словари и множества." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Последовательности\n", "
\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", "\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", "\n", "\n", "\n", "\n", "\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", "\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", "\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", "\n", "\n", "\n", "\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| Синтаксис | Описание |
|---|---|
L.append(x) | Добавляет элемент x в конец списка L |
L.count(x) | Возвращает число вхождений элемента x в список L |
L.extend(m) или L += m | Добавляет в конец списка L все элементы итерируемого объекта m |
L.index(x, start, end) | Возвращает индекс самого первого (слева) вхождения элемента x в список L (или в срез start:end списка L); в противном случае возбуждает исключение ValueError |
L.insert(i, x) | Вставляет элемент x в список L в позицию int i |
L.pop() | Удаляет самый последний элемент из списка L и возвращает его в качестве результата |
L.pop(i) | Удаляет из списка L элемент с индексом int i и возвращает его в качестве результата |
L.remove(x) | Удаляет самый первый (слева) найденный элемент x из списка L или возбуждает исключение ValueError, если элемент xне будет найден |
L.reverse() | Переставляет в памяти элементы списка в обратном порядке |
L.sort() | Сортирует список в памяти. Этот метод принимает те же необязательные аргументы key и reverse что и встроенная функция sorted()` |
| Синтаксис | Описание |
|---|---|
s.add(x) | Добавляет элементы x во множество s, если они отсутствуют в s |
s.clear() | Удаляет все элементы из множества s |
s.difference(t)или s-t | Возвращает новое множество включающее элементы множества s, которые отсутствуют в множестве t |
s.difference_update(t) или s-=t | Удаляет из множества s все элементы, присутствующие в множестве t |
s.discard(x) | Удаляет элемент x из множества s, если он присутствует в множестве s |
s.intersection(t) или s & t | Возвращает новое множество, включающее элементы, присутствующие одновременно в множествах s и t |
s.intersection_update(t) или s &= t | Оставляет во множестве s пересечение множеств s и t |
s.isdisjoint(t) | Возвращает True, если множества s и t не имеют общих элементов |
s.issubset(t) или s <= t | Возвращает True, если множество s эквивалентно множеству t или является его подмножеством; чтобы проверить, является ли множество s только подмножеством множества t, следует использовать проверку s < t |
s.issuperset(t) или s >= t | Возвращает True, если множество s эквивалентно множеству t или является его надмножеством; чтобы проверить, является ли множество s только надмножеством множества t, следует использовать проверку s > t |
s.pop() | Возвращает и удаляет случайный элемент множества s или возбуждает исключение KeyError, если s – это пустое множество |
s.remove(x) | Удаляет элемент x из множества s или возбуждает исключение KeyError, если элемент x отсутствует в множестве s |
s.symmetric_difference(t) или s ^ t | Возвращает новое множество, включающее все элементы, присутствующие в множествах s и t, за исключением элементов, присутствующих в обоих множествах одновременно |
s.symmetric_difference_update(t) или s ^= t | Возвращает в множестве s результат строгой дизъюнкции множеств s и t |
s.union(t) или s | t | Возвращает новое множество, включающее все элементы множества s и все элементы множества t, отсутствующие в множестве s |
s.update(t) или s |= t | Добавляет во множество s все элементы множества t, отсутствующие в множестве s |
| Синтаксис | Описание |
|---|---|
d.clear() | Удаляет все элементы из словаря d |
d.copy() | Возвращает поверхностную копию словаря d |
d.fromkeys(s, v) | Возвращает словарь типа dict, ключами которого являются элементы последовательности s значениями либо None либо v, если аргумент v определен |
d.get(k) | Возвращает значение ключа k или None, если ключ k отсутствует в словаре |
d.get(k, v) | Возвращает значение ключа k или v, если ключ k отсутствует в словаре |
d.items() | Возвращает представление всех пар (ключ, значение) в словаре d |
d.keys() | Возвращает представление всех ключей словаря d |
d.pop(k) | Возвращает значение ключа k и удаляет из словаря элемент с ключом k или возбуждает исключение KeyError, если ключ k отсутствует в словаре |
d.pop(k, v) | Возвращает значение ключа k и удаляет из словаря элемент с ключом k или возвращает значение v, если ключ k отсутствует в словаре |
d.popitem() | Возвращает и удаляет произвольную пару (ключ, значение) из словаря d или возбуждает исключение KeyError, если словарь d пуст |
d.setdefault(k, v) | То же что и dict.get() за исключением того, что, если ключ k в словаре отсутствует, в словарь вставляется новый элемент с ключом k и со значением None или v, если аргумент v задан |
d.update(a) | Добавляет в словарь d пары (ключ, значение) из a, которые отсутствуют в словаре d а для каждого ключа который уже присутствует в словаре d выполняется замена соответствующим значением из a; a может быть словарем итерируемым объектом с парами (ключ значение) или именованными аргументами |
d.values() | Возвращает представление всех значений в словаре d |
| Синтаксис | Описание |
|---|---|
s + t | Возвращает конкатенацию последовательностей s и t |
s * n | Возвращает конкатенацию из int n последовательностей s |
x in i | Возвращает True, если элемент x присутствует в итерируемом объекте i, обратная проверка выполняется с помощью оператора not in |
all(i) | Возвращает True, если все элементы итерируемого объекта i в логическом контексте оцениваются как значение True |
any(i) | Возвращает True, если хотя бы один элемент итерируемого объекта i в логическом контексте оценивается как значение True |
enumerate (i,start) | Обычно используется в циклах for ... in, чтобы получить последовательность кортежей (index, item), где значения индексов начинают отсчитывать от 0 или от значения start |
len(x) | Возвращает «длину» объекта x. Если x – коллекция, то возвращаемое число представляет количество элементов. Если x – строка, то возвращаемое число представляет количество символов |
max(i, key) | Возвращает наибольший элемент в итерируемом объекте i или элемент с наибольшим значением key(item), если функция key определена |
min(i, key) | Возвращает наименьший элемент в итерируемом объекте i или элемент с наименьшим значением key(item), если функция key определена |
range(start, stop, step) | Возвращает целочисленный итератор. С одним аргументом (stop) итератор представляет последовательность целых чисел от 0 до stop-1, с двумя аргументами (start, stop) – последовательность целых чисел от start до stop-1, с тремя аргументами – последовательность целых чисел от start до stop-1 c шагом step |
reversed(i) | Возвращает итератор, который будет возвращать элементы итератора i в обратном порядке |
sorted(i, key, reverse) | Возвращает список элементов итератора i в отсортированном порядке. Аргумент key используется для выполнения сортировки DSU (Decorate, Sort, Undecorate – декорирование, сортировка, обратное декорирование). Если аргумент reverse имеет значение True, сортировка выполняется в обратном порядке |
sum(i, start) | Возвращает сумму элементов итерируемого объекта i, плюс ар гумент start (значение которого по умолчанию равно 0). Объект i не должен содержать строк |
zip(i1, ..., iN) | Возвращает итератор кортежей, используя итераторы от i1 до iN |