You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

310 lines
10 KiB
Plaintext

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

{
"cells": [
{
"cell_type": "markdown",
"id": "992d2d16",
"metadata": {},
"source": [
"# Итераторы #"
]
},
{
"cell_type": "markdown",
"id": "bc05e33b",
"metadata": {},
"source": [
"В этой лекции мы с вами поговорим про итераторы. На самом деле, с итераторами вы уже работали раньше, когда, например, использовали функцию `range` и цикл `for`. Цикл `for` позволяет вам бежать по какому-то итератору и, например, выводить все числа, как в случае с функцией `range`."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "21e1622c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"1\n",
"0\n",
"1\n",
"0\n"
]
}
],
"source": [
"for number in range(5):\n",
" print(number & 1)"
]
},
{
"cell_type": "markdown",
"id": "8fafa013",
"metadata": {},
"source": [
"Также простейшим итератором является строка или коллекция."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "5d32d58a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"112\n",
"121\n",
"116\n",
"104\n",
"111\n",
"110\n"
]
}
],
"source": [
"for letter in 'python':\n",
" print(ord(letter))"
]
},
{
"cell_type": "markdown",
"id": "89519d9e",
"metadata": {},
"source": [
"Итератор — это какой-то объект, по которому вы можете бежать, то есть итерироваться. Можно создать свой простейший итератор при помощи встроенной функции `iter` и, например, передать ей список."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "d397be0d",
"metadata": {},
"outputs": [],
"source": [
"iterator = iter([1, 2, 3])"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "67f95b4d",
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n"
]
}
],
"source": [
"print(next(iterator))"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "c59e91cc",
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2\n"
]
}
],
"source": [
"print(next(iterator))"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "e86be523",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3\n"
]
}
],
"source": [
"print(next(iterator))"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "f16a382b",
"metadata": {},
"outputs": [
{
"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<ipython-input-8-d4aa693aba62>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0miterator\u001b[0m\u001b[1;33m)\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": [
"print(next(iterator))"
]
},
{
"cell_type": "markdown",
"id": "44a59bd2",
"metadata": {},
"source": [
"Внутри протокол итерации работает очень просто. У нас каждый раз, когда мы хотим получить следующий элемент, вызывается функция `next`, и она возвращает следующий элемент. В данном случае это 1, 2 и 3. Когда у нас элементы исчерпаны, то есть итератор закончился, у нас выбрасывается исключение `StopIteration`, которое говорит о том, что, например, нужно выйти из цикла `for`."
]
},
{
"cell_type": "markdown",
"id": "c76cb6b5",
"metadata": {},
"source": [
"В Python'е вы, конечно, можете реализовать свой собственный итератор, написав класс с соответствующими магическими методами. Эти магические методы — это методы `__iter__` и `__next__`. Метод `__iter__` должен возвращать итератор в себя, а метод `__next__` определяет то, какие элементы возвращаются из итератора при, собственно, итерации.\n",
"\n",
"Давайте напишем свой класс `SquareIterator`, который будет каким-то аналогом функции `range`, только будет возвращать квадраты чисел. То есть `SquareIterator` принимает границы, внутри которых мы будем итерироваться, и возвращает квадраты чисел внутри этих лимитов."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "8edb11e1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n",
"4\n",
"9\n"
]
}
],
"source": [
"class SquareIterator:\n",
" def __init__(self, start, end):\n",
" self.current = start\n",
" self.end = end\n",
" \n",
" def __iter__(self):\n",
" return self\n",
" \n",
" def __next__(self):\n",
" if self.current >= self.end:\n",
" raise StopIteration\n",
" \n",
" result = self.current ** 2\n",
" self.current += 1\n",
" return result\n",
"\n",
"for num in SquareIterator(1, 4):\n",
" print(num)"
]
},
{
"cell_type": "markdown",
"id": "3b25cc95",
"metadata": {},
"source": [
"В функции `__init__` мы сохраняем наши пределы, и в функции `__next__` мы будем и в функции говорить о том, что происходит при вызове следующего элемента Если у нас элементы исчерпаны, то есть у нас `current` превысил `end`, мы выбрасываем исключение `StopIteration`, которое говорит протоколу итерации о том, что итерация должна закончиться. Ну а в любом другом случае мы просто возводим число в квадрат и инкрементируем счётчик."
]
},
{
"cell_type": "markdown",
"id": "7a915cf7",
"metadata": {},
"source": [
"Таким образом мы можем использовать наш класс для того, чтобы итерироваться по нему и использовать знакомый вам цикл `for` и выводить числа, например, квадраты от 1 до 4, не включая 4."
]
},
{
"cell_type": "markdown",
"id": "0f31137a",
"metadata": {},
"source": [
"Python позволяет вам создавать собственные итераторы, и иногда это бывает полезно, когда вам нужно поддержать протокол итерации в каком-то своём классе. Что интересно, можно также определить свой собственный итератор, не определяя `__iter__` и `__next__`. Это можно сделать, написав у класса метод `__getitem__`, который определяет работу класса при обращении к его объектам с помощью квадратных скобочек, то есть как к контейнеру. Мы можем создать свой собственный контейнер `IndexIterable`, который определит `__getitem__`, и вы уже в этом случае можете по нему итерироваться."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "9e0b2779",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"s\n",
"t\n",
"r\n"
]
}
],
"source": [
"class IndexIterable:\n",
" def __init__(self, obj):\n",
" self.obj = obj\n",
" \n",
" def __getitem__(self, index):\n",
" return self.obj[index]\n",
"\n",
"for letter in IndexIterable(\"str\"):\n",
" print(letter)"
]
},
{
"cell_type": "markdown",
"id": "a7128fd1",
"metadata": {},
"source": [
"Это делается довольно редко. Чаще всего для того чтобы определить свой итератор, используются именно методы `__iter__` и `__next__`. На этом всё."
]
}
],
"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.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}