{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Коллекции" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В этой лекции мы познакомимся с **коллекциями** (также называемыми **контейнерами**) - специальными классами, предназначенными для хранения множества объектов и предоставляющими доступ к ним. В Python существует несколько разновидностей коллекций - часть из них относится к встроенным типам данных, другие реализованы в отдельных модулях. Это многообразие связано с тем, что невозможно создать один тип, оптимально подходящий для всех возможных классов задач. Кроме того, многие коллекции относятся к [неизменяемым](04_Data_Types.ipynb#Изменяемость-типов-данных) типам данных и запрещают модификацию своих элементов. Это делает их непригодными для задач, где нужно часто изменять значения элементов, но позволяет эффективнее реализовать некоторые другие операции. В данной лекции мы обращаем внимание читателя на то, когда применять тот или иной тип коллекции." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Содержание лекции" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* [Общая информация](#Общая-информация)\n", "* [Последовательности](#Последовательности)\n", " * [Кортеж](#Кортеж)\n", " * [Именованный кортеж](#Именованный-кортеж)\n", " * [Список](#Список)\n", " * [Строка](#Строка)\n", " * [Распаковка последовательностей](#Распаковка-последовательностей)\n", "* [Множество](#Множество)\n", "* [Словарь](#Словарь)\n", "* [Копирование коллекций](#Копирование-коллекций)\n", "* [Генераторы](#Генераторы)\n", "* [Функции с переменным числом аргументов](#Функции-с-переменным-числом-аргументов)\n", "* [Другие типы коллекций](#Другие-типы-коллекций)\n", "* [Вопросы для самоконтроля](#Вопросы-для-самоконтроля)\n", "* [Задание](#Задание)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Общая информация" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Все коллекции относятся к итерируемым типам данных, то есть предоставляют доступ к отдельным элементам в некотором порядке. Для этого у каждого типа коллекции реализован специальный метод `__iter__`, который возвращает специфичный для нее **итератор** - специальный объект, имеющий метод `__next__`, который используется интерпретатором Python в цикле `for ... in` для получения следующего элемента. Когда метод `__next__` итератора генерирует исключение `StopIteration`, интерпретатор сам перехватывает его и завершает выполнения цикла, переходя к следующей за ним инструкции." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Помимо того, что коллекции можно использовать в цикле `for ... in`, для всех них можно применять операцию `in`, которая возвращает `True`, если указанный элемент присутствует в коллекции, и `False` в противном случае. Эту операцию часто называют операцией проверки на вхождение." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Существует несколько встроенных функций, которые могут использоваться для любого типа коллекции, рассматривамого в этой лекции. Логично перечислить их здесь, но возможно некоторые из них станут полностью понятны вам только после того, как вы изучите всю лекцию. Заметим, что необязательные параметры методов или функции здесь и далее выделяются курсивом, а с помощью символов \"...\" мы иногда указываем, что в описании были перечислены не все необязательные параметры (за дополнительной информацией в этом случае стоит обращаться к справочному руководству Python)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "|
\n", "генератор списка:\n", "[expression for item in iterable if condition]\n", "\n", "генератор множества:\n", "{expression for item in iterable if condition}\n", "\n", "генератор словаря:\n", "{key_expression:value_expression for key, value in iterable if condition}\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Все генераторы работают по одной схеме:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. В цикле обрабатываются все элементы из *iterable*.\n", "2. Если в генераторе есть предложение *if*, то для элемента вычисляется *condition*. Если в результате получается `False`, то этот элемент *iterable* игнорируется.\n", "3. Вычисляется *expression* (для словарей - *key_expression*, *value_expression*), и его результат становится элементом коллекции" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Рассмотрим простейший пример генератора. В нем мы используем функцию `range` с двумя аргументами, которые играют роль начала и конца числовой последовательности" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40]\n" ] } ], "source": [ "l = [item * 2 for item in range(10, 21)]\n", "print(l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Более полезным будет пример, в которым мы помещаем в множество только високосные года ([здесь](https://ru.wikipedia.org/wiki/%D0%92%D0%B8%D1%81%D0%BE%D0%BA%D0%BE%D1%81%D0%BD%D1%8B%D0%B9_%D0%B3%D0%BE%D0%B4#%D0%93%D1%80%D0%B8%D0%B3%D0%BE%D1%80%D0%B8%D0%B0%D0%BD%D1%81%D0%BA%D0%B8%D0%B9_%D0%BA%D0%B0%D0%BB%D0%B5%D0%BD%D0%B4%D0%B0%D1%80%D1%8C) перечислены правила, по которым можно их определить):" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1904, 1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936, 1940, 1944, 1948, 1952, 1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000]\n" ] } ], "source": [ "leap_years = {year for year in range(1900, 2001)\\\n", " if (year % 400 == 0) or (year % 100 != 0 and year % 4 == 0)}\n", "print(sorted(leap_years))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Генератор словаря может применяться для создания инвертированного словаря, в котором ключ становится значением, а значение - ключом:" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'a': 1, 'b': 2, 'c': 3}\n" ] } ], "source": [ "d = {1:'a', 2:'b', 3:'c'}\n", "inverted_d = {value:key for key, value in d.items()}\n", "print(inverted_d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Функции с переменным числом аргументов" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В Python можно создавать функции, которые принимают переменное число аргументов. Это реализовано следующим образом:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* При определении функции с помощью инструкции `def`, один из параметров может перед своим именем содержать префикс `*` или `**`.\n", "* Когда интерпретатор встречает инструкцию вызова такой функции, он вначале инициализирует все обычные параметры. Если после этого остаются какие-то позиционные аргументы, то он создает кортеж из них, а затем использует его в качестве аргумента для параметра с префиксом `*`. Аналогичным образом он поступает с оставшимися именованными параметрами, только помещает их в словарь и затем использует как аргумент для параметра с префиксом `**`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Все станет понятнее, после того, как мы рассмотрим пример такой функции, которая будет выводить все свои аргументы:" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "param1: 1\n", "param2: 2\n", "other positional params: ()\n", "other named params: {}\n", "\n", "param1: a\n", "param2: b\n", "other positional params: ()\n", "other named params: {}\n", "\n", "param1: 0.1\n", "param2: 0.2\n", "other positional params: (0.3, 0.4, 0.5)\n", "other named params: {}\n", "\n", "param1: 1\n", "param2: 2\n", "other positional params: (3, 4, 5)\n", "other named params: {'param3': 6, 'param4': 7, 'param5': 8}\n", "\n" ] } ], "source": [ "def test(param1, param2, *other_params, **other_named_params):\n", " print('param1:', param1)\n", " print('param2:', param2)\n", " print('other positional params:', other_params)\n", " print('other named params:', other_named_params)\n", " print('')\n", "\n", "# пример использования\n", "\n", "test(1, 2)\n", "test('a', param2='b')\n", "test(0.1, 0.2, 0.3, 0.4, 0.5)\n", "test(1, 2, 3, 4, 5, param3=6, param4=7, param5=8)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Давайте теперь попробуем реализовать более полезную функцию, в которую можно передавать произвольное число аргументов. Например, пусть эта функция вычисляет некоторую бинарную операцию для всех аргументов:" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "15\n", "120\n" ] } ], "source": [ "class BinaryOpError(Exception): pass\n", "\n", "def calc(op, *operands): \n", " if (len(operands) < 2):\n", " raise BinaryOpError('not enough arguments')\n", " \n", " result = operands[0]\n", " for idx in range(1, len(operands)): # начинаем цикл со второго элемента!\n", " result = op(result, operands[idx])\n", " \n", " return result\n", "\n", "\n", "# пример использования\n", "\n", "result = calc(lambda x, y: x + y,\\\n", " 0, 1, 2, 3, 4, 5)\n", "print(result)\n", "\n", "result = calc(lambda x, y: x * y,\\\n", " 1, 2, 3, 4, 5)\n", "print(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Другие типы коллекций" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В этой лекции мы рассмотрели только самые известные и часто используемые коллекции из имеющихся в Python. Упомянем вкратце еще несколько (если какая-то коллекция реализована в модуле, то имя этого модуля также указывается):" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. `bytes` - неизменяемая последовательность байтов (аналог `str`, только для байтов, а не для символов); применяется для работы с двоичными данными\n", "2. `bytearray` - изменяемая последовательность байтов; применяется для работы с двоичными данными, когда их к тому же нужно модифицировать\n", "3. `frozenset` - неизменяемое множество; применяется для тех же целей, что и обычное, но в виду того, что `frozenset` неизменяемый тип, может использоваться как элемент множества или ключ в словаре\n", "4. `collections.defaultdict` - обычный словарь, но с возможность задать значение по умолчанию для отсутствующих элементов" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Вопросы для самоконтроля" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. Что такое коллекция? Что такое итерируемый тип? Как интерпретатор выполняет цикл `for ... in`?\n", "2. Что такое последовательность? Когда их стоит использовать, а когда нет? Какие типичные операции они предоставляют?\n", "3. Что такое распаковка последовательностей?\n", "4. Для каких целей стоит использовать различные типы последовательностей?\n", "5. Что такое множество? Какие операции наиболее эффективно выполняются для него? Какое ограничение накладывается на элементы множества?\n", "6. Что такое словарь? Чем он похож на множество, а чем отличается? Что такое представление словаря?\n", "7. В чем разница между поверхностным и глубоким копированием?\n", "8. Что представляет собой генератор?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Задание" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. Проведите эксперимент, определяющий, сколько времени занимает поиск элемента в большом списке. Сравните результат с тем, что был получен для множества.\n", "2. Придумайте и реализуйте свой алгоритм сортировки списка. Сравните время его работы с временем работы `list.sort` на одних и тех же данных. Для чистоты эксперимента списки должны быть заполнены случайными целыми числами. Их можно сгенерировать с помощью функции `randint(a, b)` из модуля `random`, которая возвращает случайное число между `a` и `b`.\n", "3. Реализуйте функцию с переменным числом позиционных аргументов, являющуюся простым аналогом метода [`format`](#format). Эта функция должна принимать в качестве первого аргумента строку, в которой в произвольных местах могут быть указаны заполнители `<>`, заменяемые в результате на соответствующие позиционные аргументы." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "[Предыдущая: Классы и исключения](08_Classes_And_Exceptions.ipynb) |\n", "[Содержание](00_Overview.ipynb#Содержание) |\n", "[Следующая: Стандартная библиотека](10_Standard_Library.ipynb)" ] } ], "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.6.4" } }, "nbformat": 4, "nbformat_minor": 2 }