{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Итераторы\n", "\n", "Итераторы позволяют обойти все элементы итерируемых объектов и по всюду используются в python. \n", "\n", "```{note}\n", "Итератор (`iterator`) --- это объект, который должен определить метод `__next__` (т.е. реагировать на функцию [next](https://docs.python.org/3/library/functions.html#next)), выдавать следующий элемент коллекции по вызову этого метода и бросать исключение [StopIteration](https://docs.python.org/3/library/exceptions.html#StopIteration), когда эти элементы исчерпались. \n", "```\n", "\n", "Получить стандартный итератор по коллекции можно встроенной функцией [iter](https://docs.python.org/3/library/functions.html#iter)." ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "a_list = list(range(3))\n", "it = iter(a_list)\n", "print(it)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В ячейке выше объявлен список `a_list = [0, 1, 2]` и итератор `it` по нему. Теперь у этого итератора можно спросить следующий элемент списка функцией [next](https://docs.python.org/3/library/functions.html#next)." ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "2\n" ] }, { "ename": "StopIteration", "evalue": "", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mStopIteration\u001b[0m Traceback (most recent call last)", "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_960/1795795299.py\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 7\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 8\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 9\u001b[1;33m \u001b[0md\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mit\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;31mStopIteration\u001b[0m: " ] } ], "source": [ "a = next(it)\n", "print(a)\n", "b = next(it)\n", "print(b)\n", "c = next(it)\n", "print(c)\n", "\n", "\n", "d = next(it)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В ячейке выше функция `next` применяется к итератору `it` 4 раза. Первые 3 из них итератор успешно возвращают элементы списка `x` по порядку. На четвертом запросе следующего элемента в списке `x` уже нет, итератор бросил исключение `StopIteration`.\n", "\n", "Вообще говоря, можно изменить список при итерации по нему, но желательно по возможности избегать этого." ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 0 1 2\n" ] } ], "source": [ "it = iter(a_list)\n", "a = next(it)\n", "\n", "a_list.insert(0, 42)\n", "b, c, d = next(it), next(it), next(it)\n", "\n", "print(a, b, c, d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Цикл `for` использует итераторы для прохода по итерируемым объектам и сам вызывает функцию `next` до тех пор, пока не встретит исключение `StopIteration`.\n", "\n", "Среди итерируемых объектов, мы уже прошли\n", "- все последовательности: списки, кортежи, строки и т.п.\n", "- range;\n", "- словари и все связанные с ними `view` (`dict_keys`, `dict_values` и `dict_items`);\n", "\n", "а так же\n", "- массивы `NumPy`;\n", "- таблица и столбцы `pandas`;\n", "- и т.п. \n", "\n", "## Нестандартные итераторы\n", "\n", "В python существуют разные итераторы, которые могут пригодиться для разных целей. \n", "\n", "### reversed\n", "\n", "Встроенная функция [reversed](https://docs.python.org/3/library/functions.html#reversed) создаёт итератор, который пробегает по коллекции в обратном порядке относительно стандартного итератора `iter`." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "[3, 2, 1]\n" ] } ], "source": [ "x = [1, 2, 3]\n", "x_reversed = reversed(x)\n", "print(x_reversed)\n", "print(list(x_reversed))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### enumerate\n", "\n", "Встроенная функция [enumerate](https://docs.python.org/3/library/functions.html#enumerate) возвращает \"нумерующий итератор\", т.е. итератор, который помимо следующего элемента контейнера возвращает ещё и его номер." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['a', 'b', 'c']\n", "\n", "[(0, 'a'), (1, 'b'), (2, 'c')]\n" ] } ], "source": [ "L = list(\"abc\")\n", "L_enumerated = enumerate(L)\n", "print(L)\n", "print(L_enumerated)\n", "print(list(L_enumerated))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Часто используется в циклах, если нужен доступ к элементам контейнера по индексу. При этом, следующая запись\n", "```python\n", "for i, _ in enumerate(iterable):\n", " ...\n", "```\n", "считается предпочтительнее, чем\n", "```python\n", "for i in range(len(iterable)):\n", " ...\n", "```" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 a\n", "1 b\n", "2 c\n" ] } ], "source": [ "for i, x in enumerate(L):\n", " print(i, x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Конечно, эти итераторы можно комбинировать." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 c\n", "1 b\n", "2 a\n" ] } ], "source": [ "for i, x in enumerate(reversed(L)):\n", " print(i, x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### zip\n", "\n", "Встроенная функция [zip](https://docs.python.org/3/library/functions.html#zip) создаёт итератор по нескольким итерируемым объектам, т.е. позволяет пробежаться по нескольким контейнерам одновременно." ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 a\n", "2 b\n", "3 c\n" ] } ], "source": [ "x_list = [1, 2, 3]\n", "y_list = list(\"abc\")\n", "\n", "for x, y in zip(x_list, y_list):\n", " print(x, y)" ] }, { "cell_type": "markdown", "id": "540b0a07", "metadata": {}, "source": [ "Может быть использована для того, чтобы транспонировать матрицу или пробежаться по столбцам матрицы." ] }, { "cell_type": "code", "execution_count": 18, "id": "133af991", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2]\n", "[3, 4]\n", "\n", "(1, 3)\n", "(2, 4)\n" ] } ], "source": [ "m = [[1, 2], [3, 4]]\n", "\n", "for row in m: \n", " print(row)\n", "\n", "print()\n", "\n", "for column in zip(*m):\n", " print(column)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если коллекции внутри `zip` содержат разное количество элементов, то итерация прекратится, как только закончится элементы в самом коротком из них, а функция [zip_longest](https://docs.python.org/3/library/itertools.html#itertools.zip_longest) из модуля стандартной библиотеки [itertools](https://docs.python.org/3/library/itertools.html#module-itertools) позволяет итерировать до упора, пока хотя бы в одной последовательности остались элементы." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Модуль itertools\n", "\n", "В модуле [itertools](https://docs.python.org/3/library/itertools.html#module-itertools) определен ещё ряд полезных итераторов. Рассмотрим несколько из них.\n", "\n", "### zip_longest\n", "\n", "Если коллекции внутри `zip` содержат разное количество элементов, то итерация прекратится, как только закончится элементы в самом коротком из них, а функция [zip_longest](https://docs.python.org/3/library/itertools.html#itertools.zip_longest) из модуля `itertools` позволяет итерировать до упора, пока хотя бы в одной последовательности остались элементы.\n" ] }, { "cell_type": "code", "execution_count": 22, "id": "ad0d6c65", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0->a, 1->b, 2->c, 3->d, 4->e, 5->f, 6->g, 7->h, 8->i, 9->j, \n", "0->a, 1->b, 2->c, 3->d, 4->e, 5->f, 6->g, 7->h, 8->i, 9->j, nan->k, nan->l, nan->m, nan->n, nan->o, nan->p, nan->q, nan->r, nan->s, nan->t, nan->u, nan->v, nan->w, nan->x, nan->y, nan->z, nan->A, nan->B, nan->C, nan->D, nan->E, nan->F, nan->G, nan->H, nan->I, nan->J, nan->K, nan->L, nan->M, nan->N, nan->O, nan->P, nan->Q, nan->R, nan->S, nan->T, nan->U, nan->V, nan->W, nan->X, nan->Y, nan->Z, " ] } ], "source": [ "from string import ascii_letters, digits\n", "from itertools import zip_longest\n", "\n", "\n", "for x, y in zip(digits, ascii_letters):\n", " print(f\"{x}->{y}, \", end=\"\")\n", "\n", "print()\n", "\n", "for x, y in zip_longest(digits, ascii_letters, fillvalue=float(\"NaN\")):\n", " print(f\"{x}->{y}, \", end=\"\")\n" ] }, { "cell_type": "markdown", "id": "72feafe0", "metadata": {}, "source": [ "### product\n", "\n", "Итератор [product](https://docs.python.org/3/library/itertools.html#itertools.product) позволяет итерировать по декартову (или внешнему) произведению других итерируемых объектов. В некоторых ситуациях позволяет упростить конструкции из вложенных циклов." ] }, { "cell_type": "code", "execution_count": 9, "id": "aa1d44f6", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[(0, 'a'), (0, 'b'), (0, 'c'), (1, 'a'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'b'), (2, 'c')]\n", "[(0, 'a'), (0, 'b'), (0, 'c'), (1, 'a'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'b'), (2, 'c')]\n" ] } ], "source": [ "from itertools import product\n", "\n", "X = [0, 1, 2]\n", "Y = \"abc\"\n", "\n", "print([(x, y) for x in X for y in Y])\n", "print(list(product(X, Y)))" ] }, { "cell_type": "markdown", "id": "3ed2aa79", "metadata": {}, "source": [ "### permutations\n", "\n", "Итератор [permutations](https://docs.python.org/3/library/itertools.html#itertools.permutations) пробегается по списку всех возможных перестановок итерируемого аргумента." ] }, { "cell_type": "code", "execution_count": 27, "id": "f5b6368a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[('a', 'b', 'c'), ('a', 'c', 'b'), ('b', 'a', 'c'), ('b', 'c', 'a'), ('c', 'a', 'b'), ('c', 'b', 'a')]\n" ] } ], "source": [ "from itertools import permutations\n", "\n", "print(list(permutations(\"abc\")))" ] }, { "cell_type": "markdown", "id": "57e79ced", "metadata": {}, "source": [ "```{note}\n", "Сгенерировать случайную перестановку можно методом [shuffle](https://docs.python.org/3/library/random.html#random.shuffle) или методом [sample](https://docs.python.org/3/library/random.html#random.sample) при `k` равном длине последовательности. `shuffle` случайным образом тасует элементы на месте, а `sample` возвращает новый список.\n", "```" ] }, { "cell_type": "markdown", "id": "792136f7", "metadata": {}, "source": [ "### combinations и combinations_with_replacements\n", "\n", "Метод [combinations](https://docs.python.org/3/library/itertools.html#itertools.combinations) возвращает все возможные способы выбрать `r` элементов итерируемого объекта без повторов. \n" ] }, { "cell_type": "code", "execution_count": 29, "id": "d645919b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 and 1\n", "0 and 2\n", "1 and 2\n" ] } ], "source": [ "from itertools import combinations\n", "\n", "for x, y in combinations(range(3), 2):\n", " print(f\"{x} and {y}\") \n" ] }, { "cell_type": "markdown", "id": "f4c644b4", "metadata": {}, "source": [ "Метод [combinations_with_replacements](https://docs.python.org/3/library/itertools.html#itertools.combinations_with_replacement) делает то же самое, но допускает повторы." ] }, { "cell_type": "code", "execution_count": 30, "id": "ad7b8f28", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 and 0\n", "0 and 1\n", "0 and 2\n", "1 and 1\n", "1 and 2\n", "2 and 2\n" ] } ], "source": [ "from itertools import combinations_with_replacement\n", "\n", "for x, y in combinations_with_replacement(range(3), 2):\n", " print(f\"{x} and {y}\") " ] } ], "metadata": { "interpreter": { "hash": "8617202e12f254480e1fae3258716b685f1a56bcbf234a446366b4fd3345ed22" }, "kernelspec": { "display_name": "Python 3.8.10 64-bit", "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.10" } }, "nbformat": 4, "nbformat_minor": 5 }