SlideShare una empresa de Scribd logo
1 de 45
Descargar para leer sin conexión
Лекция 8: Итераторы, генераторы и модуль
itertools
Сергей Лебедев
sergei.a.lebedev@gmail.com
26 октября 2015 г.
Итераторы
Цикл for и модуль dis
• Напоминание: оператор for в Python работает с любой
последовательностью.
>>> import dis
>>> dis.dis("for x in xs: do_something(name)")
0 SETUP_LOOP 24 (to 27)
3 LOAD_NAME 0 (xs)
6 GET_ITER
>> 7 FOR_ITER 16 (to 26)
10 STORE_NAME 1 (x)
[...]
>> 26 POP_BLOCK
>> 27 LOAD_CONST 0 (None)
30 RETURN_VALUE
• Инструкция GET_ITER вызывает у аргумента оператора for
метод __iter__, который возвращает итератор.
• Инструкция FOR_ITER вызывает метод __next__ у
итератора до тех пор, пока не будет поднято исключение
StopIteration.
1 / 41
Протокол итераторов
• Протокол итераторов состоит из двух методов:
• Метод __iter__ возвращает экземпляр класса,
реализующего протокол итераторов, например, self.
• Метод __next__ возвращает следующий по порядку
элемент итератора. Если такого элемента нет, то метод
должен поднять исключение StopIteration.
• Важный инвариант метода __next__: если метод поднял
исключение StopIteration, то все последующие вызовы
метода __next__ тоже должны поднимать исключение.
• В отличие от, например, Java в Python iterator также
является iterable.
2 / 41
Коллекции и итераторы
• Для коллекций обычно нет смысла реализовывать
протокол итераторов целиком, достаточно реализовать
только метод __iter__.
• Иногда элементы коллекции можно перечислить более
чем одним способом. В этом случае удобно реализовывать
дополнительные методы, возвращающие итераторы:
class BinaryTree:
def __iter__(self):
return self.inorder_iter()
def preorder_iter(self):
# ...
def inorder_iter(self):
return InOrderIterator(self)
def postorder_iter(self):
# ...
3 / 41
Функции iter и next
• У функции iter две формы вызова:
• принимает итератор и вызывает у него метод __iter__,
• принимает функцию и терминальное значение и вызывает
функцию до тех пор, пока она не вернёт нужное значение1
:
from functools import partial
with open(path, "rb") as handle:
read_block = partial(handle.read, 64)
for block in iter(read_block, ""):
do_something(block)
• Функция next принимает итератор и вызывает у него
метод __next__. Можно также указать значение, которое
нужно вернуть в случае возникновения исключения
StopIteration:
>>> next(iter([1, 2, 3]))
1
>>> next(iter([]), 42)
42
1
http://bit.ly/beautiful-python
4 / 41
“Семантика” оператора for
• Напоминание:
for x in xs:
do_something(x)
• Процесс исполнения оператора for можно концептуально
записать так:
it = iter(xs)
while True:
try:
x = next(it)
except StopIteration:
break
do_something(x)
5 / 41
Протокол итераторов и операторы in и not in
• Операторы in и not in используют “магический” метод
__contains__, который возвращает True, если переданный
элемент содержится в экземпляре класса.
• По умолчанию метод __contains__ реализован через
протокол итераторов:
class object:
# ...
def __contains__(self, target):
for item in self:
if item == target:
return True
return False
• Пример:
>>> id = Identity()
>>> 5 in id # ≡ id.__contains__(5)
True
>>> 42 not in id # ≡ not id.__contains__(42)
True
6 / 41
Протокол итераторов и реализация “по умолчанию”
• В Python предусмотрен упрощённый вариант реализации
протокола итераторов с использованием метода
__getitem__.
• Метод __getitem__ принимает один аргумент — индекс
элемента в последовательности и
• либо возвращает элемент, соответствующий индексу,
• либо поднимает IndexError, если элемента с таким
индексом нет.
• Пример:
>>> class Identity:
... def __getitem__(self, idx):
... if idx > 5:
... raise IndexError(idx)
... return idx
...
>>> list(Identity())
[0, 1, 2, 3, 4, 5]
7 / 41
“Семантика” упрощённого протокола итераторов: seq_iter
class seq_iter:
def __init__(self, instance):
self.instance = instance
self.idx = 0
def __iter__(self):
return self
def __next__(self):
try:
res = self.instance[self.idx]
except IndexError:
raise StopIteration
self.idx += 1
return res
8 / 41
“Семантика” упрощённого протокола итераторов: object
class object:
# ...
def __iter__(self):
if not hasattr(self, "__getitem__"):
cls = self.__class__
msg = "{} object is not iterable"
raise TypeError(msg.format(cls.__name__))
return seq_iter(self)
9 / 41
Резюме: итераторы
• В Python iterator также является iterable.
• Итератор — это экземпляр класса, который реализует два
метода __init__ и __next__.
• Альтернативно можно воспользоваться реализацией этих
методов по умолчанию и определить метод __getitem__.
• Протокол итераторов используется:
• оператором for,
• операторами in и not in.
• Протокол итераторов реализуется всеми встроенными
коллекциями, а также, например, файлами и объектами
типа map, filter и zip.
10 / 41
Генераторы
Что такое генератор?2
• Генератор — это функция, которая использует не только
оператор return, но и оператор yield.
• В результате выполнения оператора yield работа функции
приостанавливается, а не прерывается, как при
использовании оператора return.
• Пример:
>>> def g():
... print("Started")
... x = 42
... yield x
... x += 1
... yield x
... print("Done")
>>> type(g)
<class 'function'>
>>> gen = g()
>>> type(gen)
<class 'generator'>
>>> next(gen)
Starting ...
42
>>> next(gen)
43
2
http://python.org/dev/peps/pep-0255
11 / 41
Примеры генераторов: unique
>>> def unique(iterable, seen=None):
... seen = set(seen or [])
... for item in iterable:
... if item not in seen:
... seen.add(item)
... yield item
...
>>> xs = [1, 1, 2, 3]
>>> unique(xs)
<generator object unique at 0x1027c5798>
>>> list(unique(xs))
[1, 2, 3]
>>> 1 in unique(xs)
True
12 / 41
Примеры генераторов: map
>>> def map(func, iterable, *rest):
... for args in zip(iterable, *rest):
... yield func(*args)
...
>>> xs = range(5)
>>> map(lambda x: x * x, xs)
<generator object map at 0x103122510>
>>> list(map(lambda x: x * x, xs))
[0, 1, 4, 9, 16]
>>> 9 in map(lambda x: x * x, xs)
True
13 / 41
Примеры генераторов: chain
>>> def chain(*iterables):
... for iterable in iterables:
... for item in iterable:
... yield item
...
>>> xs = range(3)
>>> ys = [42]
>>> chain(xs, ys)
<generator object chain at 0x10311d708>
>>> list(chain(xs, ys))
[0, 1, 2, 3, 42]
>>> 42 in chain(xs, ys)
True
14 / 41
Примеры генераторов: count и enumerate
>>> def count(start=0):
... while True:
... yield start
... start += 1
...
>>> next(count())
0
>>> counter = count()
>>> next(counter)
0
>>> next(counter)
1
>>> def enumerate(iterable, start=0):
... pass # как?
...
>>> next(enumerate(count(42)))
(0, 42)
15 / 41
Переиспользование генераторов
• Основное правило переиспользования генераторов: не
делайте этого.
>>> def g():
... yield 42
...
>>> gen = g()
>>> list(gen)
[42]
>>> list(gen) # не тут-то было!
[]
• Если вы хотите переиспользовать генератор, подумайте
ещё раз.
• Если вы уверены, что без переиспользования не обойтись,
воспользуйтесь функцией tee из модуля itertools.
16 / 41
Коллекции и генераторы
• Генераторы позволяют компактно реализовывать метод
__iter__ у коллекций.
• Рассмотрим уже знакомый нам класс бинарного дерева:
class BinaryTree:
def __init__(self, value, left=None, right=None):
self.value = value
self.left, self.right = left, right
def __iter__(self): # inorder
for node in self.left:
yield node.value
yield self.value
for node in self.right:
yield node.value
• Плюс генераторов в том, что они позволяют обойтись без
лишних классов, например, InOrderIterator.
17 / 41
Выражения-генераторы
• Напоминание: в Python есть генераторы списков, множеств
и словарей.
• Выражения-генераторы работают аналогичным образом,
но не порождают коллекцию в процессе работы:
>>> gen = (x ** 2 for x in range(10**42) if x % 2 == 1)
>>> gen
<generator object <genexpr> at 0x10311d708>
>>> next(gen)
1
>>> list(filter(lambda x: x % 2 == 1,
... (x ** 2 for x in range(10))))
[1, 9, 25, 49, 81]
• Если выражение-генератор — единственный аргумент
функции, скобки можно опустить:
>>> sum(x ** 2 for x in range(10) if x % 2 == 1)
165
18 / 41
Выражение yield
• Оператор yield можно использовать как выражение:
>>> def g():
... res = yield # точка входа 1
... print("Got {!r}".format(res))
... res = yield 42 # точка входа 2
... print("Got {!r}".format(res))
...
>>> gen = g()
>>> next(gen) # "промотаем" до первого yield
>>> next(gen) # "промотаем" до второго yield
Got 'None'
42
>>> next(gen) # выполним оставшуюся часть генератора
Got 'None'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
• На первый взгляд выражение yield выглядит бесполезно,
но первое впечатление обманчиво.
19 / 41
Интерфейс генераторов: send
• Метод send возобновляет выполнение генератора и
“отправляет” свой аргумент в следующий yield:
>>> gen = g()
>>> gen.send("foobar")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't send [...] to a just-started generator
• Чтобы инициализировать генератор нужно “отправить” ему
None. Функция next делает ровно это:
>>> gen = g()
>>> next(gen)
• Результатом метода send является следующее значение
генератора или исключение StopIteration, если такого
исключения нет.
>>> gen = g()
>>> gen.send(None) # ≡ next(gen)
>>> gen.send("foobar")
Got 'foobar'
42
20 / 41
Интерфейс генераторов: throw
• Метод throw поднимает переданное исключение в месте,
где генератор приостановил исполнение и возвращает
следующее значение генератора.
>>> def g():
... try:
... yield 42
... except Exception as e:
... yield e
...
>>> gen = g()
>>> next(gen)
42
>>> gen.throw(ValueError, "something is wrong")
ValueError('something is wrong',)
>>> gen.throw(RuntimeError, "another error")
???
• Если генератор не обработал брошенное в него
исключение, то выполнение генератора прекращается и
исключение передаётся наверх по стеку вызовов.
21 / 41
Интерфейс генераторов: close
• Метод close поднимает специальное исключение
GeneratorExit в месте, где генератор приостановил
исполнение:
>>> def g():
... try:
... yield 42
... finally:
... print("Done")
...
>>> gen = g()
>>> next(gen)
42
>>> gen.close()
Done
• Если всё хорошо, то метод close завершает работу
генератора и ничего не возвращает.
• Что может пойти не так? Генератор может обработать
исключение GeneratorExit и поднять другое исключение.
22 / 41
Генераторы ∼ сопрограммы aka coroutines3
• Сопрограмма — это программа, которая может иметь
больше одной точки входа, а также поддерживает
остановку и продолжение с сохранением состояния.
• Звучит как определение генератора наоборот:
>>> def grep(pattern):
... print("Looking for {!r}".format(pattern))
... while True:
... line = yield
... if pattern in line:
... print(line)
...
>>> gen = grep("Gotcha!")
>>> next(gen)
Looking for 'Gotcha!'
>>> gen.send("This line doesn't have 
... what we're looking for")
>>> gen.send("This one does. Gotcha!")
This one does. Gotcha!
3
http://dabeaz.com/coroutines
23 / 41
Инициализация сопрограмм
• Прежде, чем начать работать с сопрограммой, её нужно
инициализировать с помощью вызова функции next.
• Объявим декоратор coroutine, который скроет эту деталь
реализации:
>>> def coroutine(g):
... @functools.wraps(g)
... def inner(*args, **kwargs):
... gen = g()
... next(gen)
... return gen
... return inner
...
>>> grep = coroutine(grep)
>>> gen = grep("Gotcha!")
>>> gen.send("One more line for ya!")
• Зачем это всё нужно? Ответ в домашнем задании.
24 / 41
Генераторы ∼ легкие потоки aka green threads4
Не сейчас
4
http://dabeaz.com/coroutines
25 / 41
Оператор yield from5
• Оператор yield from позволяет делегировать выполнение
другому генератору:
def chain(*iterables):
for iterable in iterables:
yield from iterable
• Любые вызовы методов send и throw у родительского
генератора будут переданы вложенному генератору без
изменений.
5
http://python.org/dev/peps/pep-0380
26 / 41
Оператор return и исключение StopIteration
• Кроме оператора yield в теле генератора можно
использовать оператор return.
• На человеческом языке использование return означает:
«У меня больше нет элементов, извини, возьми лучше вот
это.»
>>> def g():
... yield 42
... return [] # держи!
...
>>> gen = g()
>>> next(gen)
42
>>> next(gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration: [] # вот и оно.
27 / 41
return ≡ raise StopIteration
• Несмотря на схожесть, использование оператора return в
генераторе не эквивалентно поднятию исключения
StopIteration.
• Контрпример:
def g():
try:
yield 42
raise StopIteration([]) # ≡ return []
except Exception as e:
pass
28 / 41
Выражение yield from
• Оператор yield from, как и оператор yield, можно
использовать в качестве выражения.
• При этом значением выражения yield from будет
значение атрибута value у поднятого вложенным
генератором исключения StopIteration:
>>> def f():
... yield 42
... return []
...
>>> def g():
... res = yield from f()
... print("Got {!r}".format(res))
...
>>> gen = g()
>>> next(gen)
42
>>> next(gen, None)
Got []
29 / 41
Менеджеры контекста и генераторы: мотивация
• Протокол менеджеров контекста требует реализации двух
методов: __enter__ и __exit__,
• Если мы хотим, чтобы у менеджера было какое-то
состояние, то мы вынуждены также добавить метод
__init__.
• В итоге получаем:
class cd:
def __init__(self, path):
self.path = path
def __enter__(self):
self.saved_cwd = os.getcwd()
os.chdir(self.path)
def __exit__(self, *exc_info):
os.chdir(self.saved_cwd)
30 / 41
Менеджеры контекста и генераторы: @contextmanager
• Декоратор contextmanager из модуля contextlib
принимает генератор специального вида и строит по нему
менеджер контекста.
from contextlib import contextmanager
@contextmanager
def cd(path): # __init__
old_path = os.getcwd() # __enter__
os.chdir(path)
try:
yield # ---------
finally:
os.chdir(old_path) # __exit__
• Генераторы позволяют сократить количество
синтаксического шума при реализации менеджеров
контекста.
31 / 41
Ещё один пример использования @contextmanager
Метод __enter__, построенный декоратором contextmanager,
возвращает аргумент оператора yield:
import tempfile
import shutil
@contextmanager
def tempdir(): # __init__
outdir = tempfile.mkdtemp() # __enter__
try:
yield outdir # ---------
finally:
shutil.rmtree(outdir) # __exit__
with tempdir() as path:
print(path) # ==> /tmp/tmpvfzsmvsv
32 / 41
Генераторы: резюме
• Генератор в Python — это функция, которая использует
операторы yield или yield from.
• В мире Python генераторы вездесущи не менее, чем
любимые всеми декораторы.
• Мы поговорили о том, что генераторы можно использовать
• как итераторы,
• как сопрограммы,
• как легкие потоки,
• для компактной реализации менеджеров контекста.
33 / 41
Модуль
itertools
Модуль itertools: islice
• Функция islice обобщает понятие слайса на
произвольный итератор:
>>> from itertools import islice
>>> xs = range(10)
>>> list(islice(xs, 3)) # ≡ xs[:3]
[0, 1, 2]
>>> list(islice(xs, 3, None)) # ≡ xs[3:]
[3, 4, 5, 6, 7, 8, 9]
>>> list(islice(xs, 3, 8, 2)) # ≡ xs[3:8:2]
[3, 5, 7]
• Прелесть функций из модуля itertools в том, что с
помощью них легко выражаются самые разнообразные
операции над последовательностями.
Вопрос
Как будет выглядеть функция drop, “выкидывающая” префикс
длины n из переданного ей итератора?
34 / 41
Модуль itertools: бесконечные итераторы
• Для удобства реализуем родственника функции drop:
функцию take, которая строит список из более, чем n
первых элементов переданного ей итератора.
>>> def take(n, iterable):
... return list(islice(iterable, n))
...
>>> list(take(range(10), 3))
[0, 1 2]
• Названия бесконечных итераторов говорят сами за себя:
>>> from itertools import count, cycle, repeat
>>> take(3, count(0, 5))
[0, 5, 10]
>>> take(3, cycle([1, 2, 3]))
[1, 2, 3]
>>> take(3, repeat(42))
[42, 42, 42]
>>> take(3, repeat(42, 2)) # не совсем ∞
[42, 42]
35 / 41
Модуль itertools: dropwhile и takewhile
• Функции dropwhile и takewhile обобщают логику функций
drop и take на произвольный предикат.
• Обратите внимание, что обе функции возвращают
итератор, а не список, как реализованная нами функция
take:
>>> from itertools import dropwhile, takewhile
>>> list(dropwhile(lambda x: x < 5, range(10)))
[5, 6, 7, 8, 9]
>>> it = takewhile(lambda x: x < 5, range(10))
>>> it
<itertools.takewhile object at 0x1022ca748>
>>> list(it)
[0, 1, 2, 3, 4]
36 / 41
Модуль itertools: chain
• В модуле itertools реализован уже знакомый нам
генератор chain, который конкатенирует произвольное
число итераторов:
>>> from itertools import chain
>>> take(5, chain(range(2), range(5, 10)))
[0, 1, 5, 6, 7]
• Сконкатенировать итератор итераторов (!) можно с
помощью метода chain.from_iterable:
>>> it = (range(x, x ** x) for x in range(2, 4))
>>> take(5, chain.from_iterable(it))
[2, 3, 3, 4, 5]
Вопрос
Чем chain.from_iterable(it) отличается от chain(*it)?
37 / 41
Модуль itertools: tee
• Функция tee создаёт n независимых копий переданного ей
итератора:
>>> from itertools import tee
>>> it = range(3)
>>> a, b, c = tee(it, 3)
>>> list(a), list(b), list(c)
([0, 1, 2], [0, 1, 2], [0, 1, 2])
• Использовать it после копирования не рекомендуется,
потому что в этом случае скопированные итераторы
a, b, c могут пропустить элемент:
>>> it = iter(range(3))
>>> a, b = tee(it, 2)
>>> used = list(it)
>>> list(a), list(b)
([], [])
Вопрос
Что изменится, если убрать вызов функции iter из второго
примера?
38 / 41
Модуль itertools: комбинаторные итераторы
В модуле itertools в виде итераторов реализованы полезные
комбинаторные операции, например:
• декартово произведение итераторов,
>>> list(itertools.product("AB", repeat=2))
[('A', 'A'), ('A', 'B'), ('B', 'A'), ('B', 'B')]
>>> list(itertools.product("AB", repeat=3))
[('A', 'A', 'A'), ('A', 'A', 'B'), ('A', 'B', 'A'), ...]
• перестановки элементов итератора,
>>> list(itertools.permutations("AB"))
[('A', 'B'), ('B', 'A')]
• сочетания (с повторениями и без) из элементов итератора.
>>> from itertools import combinations, 
... combinations_with_replacement
>>> list(combinations("ABC", 2))
[('A', 'B'), ('A', 'C'), ('B', 'C')]
>>> list(combinations_with_replacement("ABC", 2))
[('A', 'A'), ('A', 'B'), ('A', 'C'),
('B', 'B'), ('B', 'C'), ('C', 'C')]
39 / 41
Комбинаторные итераторы и функция build_graph
def build_graph(words, mismatch_percent):
g = ...
n_words = len(words)
for u, v in itertools.combinations(range(n_words), 2):
if len(words[u]) != len(words[v]):
continue
distance = hamming(words[u], words[v])
# ...
return g
Вопрос
Выглядит неплохо, но можно лучше. Как?
40 / 41
Модуль itertools: резюме
• Модуль itertools предоставляет обширный набор
компонент для реализации операций над
последовательностями.
• Мы обсудили:
• islice,
• бесконечные итераторы count, cycle, repeat,
• chain,
• tee,
• комбинаторные итераторы product, permutations,
combinations и combinations_with_replacement.
41 / 41

Más contenido relacionado

La actualidad más candente

Лекция 11. Тестирование.
Лекция 11. Тестирование.Лекция 11. Тестирование.
Лекция 11. Тестирование.Roman Brovko
 
Лекция 7. Исключения и менеджеры контекста.
Лекция 7. Исключения и менеджеры контекста.Лекция 7. Исключения и менеджеры контекста.
Лекция 7. Исключения и менеджеры контекста.Roman Brovko
 
Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Roman Brovko
 
Лекция 4. Строки, байты, файлы и ввод/вывод.
 Лекция 4. Строки, байты, файлы и ввод/вывод. Лекция 4. Строки, байты, файлы и ввод/вывод.
Лекция 4. Строки, байты, файлы и ввод/вывод.Roman Brovko
 
Красота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonКрасота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonPython Meetup
 
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Яковенко Кирилл
 
Pyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPython Meetup
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Python Meetup
 
Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?PyNSK
 
Оптимизация производительности Python
Оптимизация производительности PythonОптимизация производительности Python
Оптимизация производительности PythonPyNSK
 
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в DjangoMoscowDjango
 
Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование Sergey Schetinin
 
Мир Python функционалим с помощью библиотек
Мир Python  функционалим с помощью библиотекМир Python  функционалим с помощью библиотек
Мир Python функционалим с помощью библиотекPyNSK
 
9. java lecture library
9. java lecture library9. java lecture library
9. java lecture libraryMERA_school
 
Python и его тормоза
Python и его тормозаPython и его тормоза
Python и его тормозаAlexander Shigin
 
Профилирование и отладка Django
Профилирование и отладка DjangoПрофилирование и отладка Django
Профилирование и отладка DjangoVladimir Rudnyh
 
10. java lecture generics&collections
10. java lecture generics&collections10. java lecture generics&collections
10. java lecture generics&collectionsMERA_school
 

La actualidad más candente (17)

Лекция 11. Тестирование.
Лекция 11. Тестирование.Лекция 11. Тестирование.
Лекция 11. Тестирование.
 
Лекция 7. Исключения и менеджеры контекста.
Лекция 7. Исключения и менеджеры контекста.Лекция 7. Исключения и менеджеры контекста.
Лекция 7. Исключения и менеджеры контекста.
 
Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.
 
Лекция 4. Строки, байты, файлы и ввод/вывод.
 Лекция 4. Строки, байты, файлы и ввод/вывод. Лекция 4. Строки, байты, файлы и ввод/вывод.
Лекция 4. Строки, байты, файлы и ввод/вывод.
 
Красота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonКрасота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки Python
 
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3
 
Pyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPyton – пробуем функциональный стиль
Pyton – пробуем функциональный стиль
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?
 
Оптимизация производительности Python
Оптимизация производительности PythonОптимизация производительности Python
Оптимизация производительности Python
 
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в Django
 
Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование
 
Мир Python функционалим с помощью библиотек
Мир Python  функционалим с помощью библиотекМир Python  функционалим с помощью библиотек
Мир Python функционалим с помощью библиотек
 
9. java lecture library
9. java lecture library9. java lecture library
9. java lecture library
 
Python и его тормоза
Python и его тормозаPython и его тормоза
Python и его тормоза
 
Профилирование и отладка Django
Профилирование и отладка DjangoПрофилирование и отладка Django
Профилирование и отладка Django
 
10. java lecture generics&collections
10. java lecture generics&collections10. java lecture generics&collections
10. java lecture generics&collections
 

Destacado

01 - Практика UML. Нужен ли UML?
01 - Практика UML. Нужен ли UML?01 - Практика UML. Нужен ли UML?
01 - Практика UML. Нужен ли UML?Roman Brovko
 
09 - Практика UML. Use Case диаграммы
09 - Практика UML. Use Case диаграммы09 - Практика UML. Use Case диаграммы
09 - Практика UML. Use Case диаграммыRoman Brovko
 
13 - Практика UML. Переход к разработке
13 - Практика UML. Переход к разработке13 - Практика UML. Переход к разработке
13 - Практика UML. Переход к разработкеRoman Brovko
 
02 - Практика UML. Уровни приложения
02 - Практика UML. Уровни приложения02 - Практика UML. Уровни приложения
02 - Практика UML. Уровни приложенияRoman Brovko
 
03 - Практика UML. Прецеденты
03 - Практика UML. Прецеденты03 - Практика UML. Прецеденты
03 - Практика UML. ПрецедентыRoman Brovko
 
12 - Практика UML. Создание wireframe
12 - Практика UML. Создание wireframe12 - Практика UML. Создание wireframe
12 - Практика UML. Создание wireframeRoman Brovko
 
04 - Практика UML. Описание прецедентов
04 - Практика UML. Описание прецедентов04 - Практика UML. Описание прецедентов
04 - Практика UML. Описание прецедентовRoman Brovko
 

Destacado (7)

01 - Практика UML. Нужен ли UML?
01 - Практика UML. Нужен ли UML?01 - Практика UML. Нужен ли UML?
01 - Практика UML. Нужен ли UML?
 
09 - Практика UML. Use Case диаграммы
09 - Практика UML. Use Case диаграммы09 - Практика UML. Use Case диаграммы
09 - Практика UML. Use Case диаграммы
 
13 - Практика UML. Переход к разработке
13 - Практика UML. Переход к разработке13 - Практика UML. Переход к разработке
13 - Практика UML. Переход к разработке
 
02 - Практика UML. Уровни приложения
02 - Практика UML. Уровни приложения02 - Практика UML. Уровни приложения
02 - Практика UML. Уровни приложения
 
03 - Практика UML. Прецеденты
03 - Практика UML. Прецеденты03 - Практика UML. Прецеденты
03 - Практика UML. Прецеденты
 
12 - Практика UML. Создание wireframe
12 - Практика UML. Создание wireframe12 - Практика UML. Создание wireframe
12 - Практика UML. Создание wireframe
 
04 - Практика UML. Описание прецедентов
04 - Практика UML. Описание прецедентов04 - Практика UML. Описание прецедентов
04 - Практика UML. Описание прецедентов
 

Similar a Лекция 8. Итераторы, генераторы и модуль itertools.

Теории и практики фунционального программирования - GDG D2D
Теории и практики фунционального программирования - GDG D2DТеории и практики фунционального программирования - GDG D2D
Теории и практики фунционального программирования - GDG D2D0xffAA
 
Теории и практики функционального программирования.
Теории и практики функционального программирования.Теории и практики функционального программирования.
Теории и практики функционального программирования.Dev2Dev
 
Lambdas in java 8
Lambdas in java 8Lambdas in java 8
Lambdas in java 8chashnikov
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3etyumentcev
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3Eugeniy Tyumentcev
 
Сверхоптимизация кода на Python
Сверхоптимизация кода на PythonСверхоптимизация кода на Python
Сверхоптимизация кода на Pythonru_Parallels
 
Сверхоптимизация кода на Python
Сверхоптимизация кода на PythonСверхоптимизация кода на Python
Сверхоптимизация кода на PythonCodeFest
 
CPU Performance in Java.
CPU Performance in Java.CPU Performance in Java.
CPU Performance in Java.Dzmitry Hil
 
Статический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановСтатический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановYandex
 
Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10Dima Dzuba
 
Yuriy Sherstyuk - Algorithms in Front End: from V8 to VDOM
Yuriy Sherstyuk - Algorithms in Front End: from V8 to VDOMYuriy Sherstyuk - Algorithms in Front End: from V8 to VDOM
Yuriy Sherstyuk - Algorithms in Front End: from V8 to VDOMOdessaJS Conf
 
Лекция о языке программирования Haskell
Лекция о языке программирования HaskellЛекция о языке программирования Haskell
Лекция о языке программирования Haskellhusniyarova
 
functional patterns - dotnetconf'11
functional patterns - dotnetconf'11functional patterns - dotnetconf'11
functional patterns - dotnetconf'110xffAA
 
лекция 3. программирование циклов
лекция 3. программирование цикловлекция 3. программирование циклов
лекция 3. программирование цикловstudent_kai
 
C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.Igor Shkulipa
 
Cocoheads Moscow September
Cocoheads Moscow SeptemberCocoheads Moscow September
Cocoheads Moscow SeptemberAlexander Zimin
 

Similar a Лекция 8. Итераторы, генераторы и модуль itertools. (20)

Decorators' recipes
Decorators' recipesDecorators' recipes
Decorators' recipes
 
Теории и практики фунционального программирования - GDG D2D
Теории и практики фунционального программирования - GDG D2DТеории и практики фунционального программирования - GDG D2D
Теории и практики фунционального программирования - GDG D2D
 
Основы Python. Функции
Основы Python. ФункцииОсновы Python. Функции
Основы Python. Функции
 
Теории и практики функционального программирования.
Теории и практики функционального программирования.Теории и практики функционального программирования.
Теории и практики функционального программирования.
 
Lambdas in java 8
Lambdas in java 8Lambdas in java 8
Lambdas in java 8
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3
 
Сверхоптимизация кода на Python
Сверхоптимизация кода на PythonСверхоптимизация кода на Python
Сверхоптимизация кода на Python
 
Сверхоптимизация кода на Python
Сверхоптимизация кода на PythonСверхоптимизация кода на Python
Сверхоптимизация кода на Python
 
Reactive extensions
Reactive extensionsReactive extensions
Reactive extensions
 
CPU Performance in Java.
CPU Performance in Java.CPU Performance in Java.
CPU Performance in Java.
 
Статический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановСтатический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий Леванов
 
Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10
 
Yuriy Sherstyuk - Algorithms in Front End: from V8 to VDOM
Yuriy Sherstyuk - Algorithms in Front End: from V8 to VDOMYuriy Sherstyuk - Algorithms in Front End: from V8 to VDOM
Yuriy Sherstyuk - Algorithms in Front End: from V8 to VDOM
 
Лекция о языке программирования Haskell
Лекция о языке программирования HaskellЛекция о языке программирования Haskell
Лекция о языке программирования Haskell
 
functional patterns - dotnetconf'11
functional patterns - dotnetconf'11functional patterns - dotnetconf'11
functional patterns - dotnetconf'11
 
лекция 3. программирование циклов
лекция 3. программирование цикловлекция 3. программирование циклов
лекция 3. программирование циклов
 
Scala on android
Scala on androidScala on android
Scala on android
 
C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.
 
Cocoheads Moscow September
Cocoheads Moscow SeptemberCocoheads Moscow September
Cocoheads Moscow September
 

Más de Roman Brovko

Individual task Networking
Individual task NetworkingIndividual task Networking
Individual task NetworkingRoman Brovko
 
Networking essentials lect3
Networking essentials lect3Networking essentials lect3
Networking essentials lect3Roman Brovko
 
Gl embedded starterkit_ethernet
Gl embedded starterkit_ethernetGl embedded starterkit_ethernet
Gl embedded starterkit_ethernetRoman Brovko
 
Networking essentials lect2
Networking essentials lect2Networking essentials lect2
Networking essentials lect2Roman Brovko
 
Networking essentials lect1
Networking essentials lect1Networking essentials lect1
Networking essentials lect1Roman Brovko
 
Bare metal training_07_spi_flash
Bare metal training_07_spi_flashBare metal training_07_spi_flash
Bare metal training_07_spi_flashRoman Brovko
 
Bare metal training_06_I2C
Bare metal training_06_I2CBare metal training_06_I2C
Bare metal training_06_I2CRoman Brovko
 
Bare metal training_05_uart
Bare metal training_05_uartBare metal training_05_uart
Bare metal training_05_uartRoman Brovko
 
Bare metal training_04_adc_temp_sensor
Bare metal training_04_adc_temp_sensorBare metal training_04_adc_temp_sensor
Bare metal training_04_adc_temp_sensorRoman Brovko
 
Bare metal training_03_timers_pwm
Bare metal training_03_timers_pwmBare metal training_03_timers_pwm
Bare metal training_03_timers_pwmRoman Brovko
 
Bare metal training_02_le_ds_and_buttons
Bare metal training_02_le_ds_and_buttonsBare metal training_02_le_ds_and_buttons
Bare metal training_02_le_ds_and_buttonsRoman Brovko
 
Bare metal training_01_hello_world
Bare metal training_01_hello_worldBare metal training_01_hello_world
Bare metal training_01_hello_worldRoman Brovko
 
Bare metal training_00_prerequisites
Bare metal training_00_prerequisitesBare metal training_00_prerequisites
Bare metal training_00_prerequisitesRoman Brovko
 
C language lect_23_advanced
C language lect_23_advancedC language lect_23_advanced
C language lect_23_advancedRoman Brovko
 
C language lect_22_advanced
C language lect_22_advancedC language lect_22_advanced
C language lect_22_advancedRoman Brovko
 
C language lect_21_advanced
C language lect_21_advancedC language lect_21_advanced
C language lect_21_advancedRoman Brovko
 
подготовка рабочего окружения
подготовка рабочего окруженияподготовка рабочего окружения
подготовка рабочего окруженияRoman Brovko
 
C language lect_20_advanced
C language lect_20_advancedC language lect_20_advanced
C language lect_20_advancedRoman Brovko
 
C language lect_19_basics
C language lect_19_basicsC language lect_19_basics
C language lect_19_basicsRoman Brovko
 

Más de Roman Brovko (20)

Individual task Networking
Individual task NetworkingIndividual task Networking
Individual task Networking
 
Networking essentials lect3
Networking essentials lect3Networking essentials lect3
Networking essentials lect3
 
Gl embedded starterkit_ethernet
Gl embedded starterkit_ethernetGl embedded starterkit_ethernet
Gl embedded starterkit_ethernet
 
Networking essentials lect2
Networking essentials lect2Networking essentials lect2
Networking essentials lect2
 
Networking essentials lect1
Networking essentials lect1Networking essentials lect1
Networking essentials lect1
 
Bare metal training_07_spi_flash
Bare metal training_07_spi_flashBare metal training_07_spi_flash
Bare metal training_07_spi_flash
 
Bare metal training_06_I2C
Bare metal training_06_I2CBare metal training_06_I2C
Bare metal training_06_I2C
 
Glesk worshop
Glesk worshopGlesk worshop
Glesk worshop
 
Bare metal training_05_uart
Bare metal training_05_uartBare metal training_05_uart
Bare metal training_05_uart
 
Bare metal training_04_adc_temp_sensor
Bare metal training_04_adc_temp_sensorBare metal training_04_adc_temp_sensor
Bare metal training_04_adc_temp_sensor
 
Bare metal training_03_timers_pwm
Bare metal training_03_timers_pwmBare metal training_03_timers_pwm
Bare metal training_03_timers_pwm
 
Bare metal training_02_le_ds_and_buttons
Bare metal training_02_le_ds_and_buttonsBare metal training_02_le_ds_and_buttons
Bare metal training_02_le_ds_and_buttons
 
Bare metal training_01_hello_world
Bare metal training_01_hello_worldBare metal training_01_hello_world
Bare metal training_01_hello_world
 
Bare metal training_00_prerequisites
Bare metal training_00_prerequisitesBare metal training_00_prerequisites
Bare metal training_00_prerequisites
 
C language lect_23_advanced
C language lect_23_advancedC language lect_23_advanced
C language lect_23_advanced
 
C language lect_22_advanced
C language lect_22_advancedC language lect_22_advanced
C language lect_22_advanced
 
C language lect_21_advanced
C language lect_21_advancedC language lect_21_advanced
C language lect_21_advanced
 
подготовка рабочего окружения
подготовка рабочего окруженияподготовка рабочего окружения
подготовка рабочего окружения
 
C language lect_20_advanced
C language lect_20_advancedC language lect_20_advanced
C language lect_20_advanced
 
C language lect_19_basics
C language lect_19_basicsC language lect_19_basics
C language lect_19_basics
 

Лекция 8. Итераторы, генераторы и модуль itertools.

  • 1. Лекция 8: Итераторы, генераторы и модуль itertools Сергей Лебедев sergei.a.lebedev@gmail.com 26 октября 2015 г.
  • 3. Цикл for и модуль dis • Напоминание: оператор for в Python работает с любой последовательностью. >>> import dis >>> dis.dis("for x in xs: do_something(name)") 0 SETUP_LOOP 24 (to 27) 3 LOAD_NAME 0 (xs) 6 GET_ITER >> 7 FOR_ITER 16 (to 26) 10 STORE_NAME 1 (x) [...] >> 26 POP_BLOCK >> 27 LOAD_CONST 0 (None) 30 RETURN_VALUE • Инструкция GET_ITER вызывает у аргумента оператора for метод __iter__, который возвращает итератор. • Инструкция FOR_ITER вызывает метод __next__ у итератора до тех пор, пока не будет поднято исключение StopIteration. 1 / 41
  • 4. Протокол итераторов • Протокол итераторов состоит из двух методов: • Метод __iter__ возвращает экземпляр класса, реализующего протокол итераторов, например, self. • Метод __next__ возвращает следующий по порядку элемент итератора. Если такого элемента нет, то метод должен поднять исключение StopIteration. • Важный инвариант метода __next__: если метод поднял исключение StopIteration, то все последующие вызовы метода __next__ тоже должны поднимать исключение. • В отличие от, например, Java в Python iterator также является iterable. 2 / 41
  • 5. Коллекции и итераторы • Для коллекций обычно нет смысла реализовывать протокол итераторов целиком, достаточно реализовать только метод __iter__. • Иногда элементы коллекции можно перечислить более чем одним способом. В этом случае удобно реализовывать дополнительные методы, возвращающие итераторы: class BinaryTree: def __iter__(self): return self.inorder_iter() def preorder_iter(self): # ... def inorder_iter(self): return InOrderIterator(self) def postorder_iter(self): # ... 3 / 41
  • 6. Функции iter и next • У функции iter две формы вызова: • принимает итератор и вызывает у него метод __iter__, • принимает функцию и терминальное значение и вызывает функцию до тех пор, пока она не вернёт нужное значение1 : from functools import partial with open(path, "rb") as handle: read_block = partial(handle.read, 64) for block in iter(read_block, ""): do_something(block) • Функция next принимает итератор и вызывает у него метод __next__. Можно также указать значение, которое нужно вернуть в случае возникновения исключения StopIteration: >>> next(iter([1, 2, 3])) 1 >>> next(iter([]), 42) 42 1 http://bit.ly/beautiful-python 4 / 41
  • 7. “Семантика” оператора for • Напоминание: for x in xs: do_something(x) • Процесс исполнения оператора for можно концептуально записать так: it = iter(xs) while True: try: x = next(it) except StopIteration: break do_something(x) 5 / 41
  • 8. Протокол итераторов и операторы in и not in • Операторы in и not in используют “магический” метод __contains__, который возвращает True, если переданный элемент содержится в экземпляре класса. • По умолчанию метод __contains__ реализован через протокол итераторов: class object: # ... def __contains__(self, target): for item in self: if item == target: return True return False • Пример: >>> id = Identity() >>> 5 in id # ≡ id.__contains__(5) True >>> 42 not in id # ≡ not id.__contains__(42) True 6 / 41
  • 9. Протокол итераторов и реализация “по умолчанию” • В Python предусмотрен упрощённый вариант реализации протокола итераторов с использованием метода __getitem__. • Метод __getitem__ принимает один аргумент — индекс элемента в последовательности и • либо возвращает элемент, соответствующий индексу, • либо поднимает IndexError, если элемента с таким индексом нет. • Пример: >>> class Identity: ... def __getitem__(self, idx): ... if idx > 5: ... raise IndexError(idx) ... return idx ... >>> list(Identity()) [0, 1, 2, 3, 4, 5] 7 / 41
  • 10. “Семантика” упрощённого протокола итераторов: seq_iter class seq_iter: def __init__(self, instance): self.instance = instance self.idx = 0 def __iter__(self): return self def __next__(self): try: res = self.instance[self.idx] except IndexError: raise StopIteration self.idx += 1 return res 8 / 41
  • 11. “Семантика” упрощённого протокола итераторов: object class object: # ... def __iter__(self): if not hasattr(self, "__getitem__"): cls = self.__class__ msg = "{} object is not iterable" raise TypeError(msg.format(cls.__name__)) return seq_iter(self) 9 / 41
  • 12. Резюме: итераторы • В Python iterator также является iterable. • Итератор — это экземпляр класса, который реализует два метода __init__ и __next__. • Альтернативно можно воспользоваться реализацией этих методов по умолчанию и определить метод __getitem__. • Протокол итераторов используется: • оператором for, • операторами in и not in. • Протокол итераторов реализуется всеми встроенными коллекциями, а также, например, файлами и объектами типа map, filter и zip. 10 / 41
  • 14. Что такое генератор?2 • Генератор — это функция, которая использует не только оператор return, но и оператор yield. • В результате выполнения оператора yield работа функции приостанавливается, а не прерывается, как при использовании оператора return. • Пример: >>> def g(): ... print("Started") ... x = 42 ... yield x ... x += 1 ... yield x ... print("Done") >>> type(g) <class 'function'> >>> gen = g() >>> type(gen) <class 'generator'> >>> next(gen) Starting ... 42 >>> next(gen) 43 2 http://python.org/dev/peps/pep-0255 11 / 41
  • 15. Примеры генераторов: unique >>> def unique(iterable, seen=None): ... seen = set(seen or []) ... for item in iterable: ... if item not in seen: ... seen.add(item) ... yield item ... >>> xs = [1, 1, 2, 3] >>> unique(xs) <generator object unique at 0x1027c5798> >>> list(unique(xs)) [1, 2, 3] >>> 1 in unique(xs) True 12 / 41
  • 16. Примеры генераторов: map >>> def map(func, iterable, *rest): ... for args in zip(iterable, *rest): ... yield func(*args) ... >>> xs = range(5) >>> map(lambda x: x * x, xs) <generator object map at 0x103122510> >>> list(map(lambda x: x * x, xs)) [0, 1, 4, 9, 16] >>> 9 in map(lambda x: x * x, xs) True 13 / 41
  • 17. Примеры генераторов: chain >>> def chain(*iterables): ... for iterable in iterables: ... for item in iterable: ... yield item ... >>> xs = range(3) >>> ys = [42] >>> chain(xs, ys) <generator object chain at 0x10311d708> >>> list(chain(xs, ys)) [0, 1, 2, 3, 42] >>> 42 in chain(xs, ys) True 14 / 41
  • 18. Примеры генераторов: count и enumerate >>> def count(start=0): ... while True: ... yield start ... start += 1 ... >>> next(count()) 0 >>> counter = count() >>> next(counter) 0 >>> next(counter) 1 >>> def enumerate(iterable, start=0): ... pass # как? ... >>> next(enumerate(count(42))) (0, 42) 15 / 41
  • 19. Переиспользование генераторов • Основное правило переиспользования генераторов: не делайте этого. >>> def g(): ... yield 42 ... >>> gen = g() >>> list(gen) [42] >>> list(gen) # не тут-то было! [] • Если вы хотите переиспользовать генератор, подумайте ещё раз. • Если вы уверены, что без переиспользования не обойтись, воспользуйтесь функцией tee из модуля itertools. 16 / 41
  • 20. Коллекции и генераторы • Генераторы позволяют компактно реализовывать метод __iter__ у коллекций. • Рассмотрим уже знакомый нам класс бинарного дерева: class BinaryTree: def __init__(self, value, left=None, right=None): self.value = value self.left, self.right = left, right def __iter__(self): # inorder for node in self.left: yield node.value yield self.value for node in self.right: yield node.value • Плюс генераторов в том, что они позволяют обойтись без лишних классов, например, InOrderIterator. 17 / 41
  • 21. Выражения-генераторы • Напоминание: в Python есть генераторы списков, множеств и словарей. • Выражения-генераторы работают аналогичным образом, но не порождают коллекцию в процессе работы: >>> gen = (x ** 2 for x in range(10**42) if x % 2 == 1) >>> gen <generator object <genexpr> at 0x10311d708> >>> next(gen) 1 >>> list(filter(lambda x: x % 2 == 1, ... (x ** 2 for x in range(10)))) [1, 9, 25, 49, 81] • Если выражение-генератор — единственный аргумент функции, скобки можно опустить: >>> sum(x ** 2 for x in range(10) if x % 2 == 1) 165 18 / 41
  • 22. Выражение yield • Оператор yield можно использовать как выражение: >>> def g(): ... res = yield # точка входа 1 ... print("Got {!r}".format(res)) ... res = yield 42 # точка входа 2 ... print("Got {!r}".format(res)) ... >>> gen = g() >>> next(gen) # "промотаем" до первого yield >>> next(gen) # "промотаем" до второго yield Got 'None' 42 >>> next(gen) # выполним оставшуюся часть генератора Got 'None' Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration • На первый взгляд выражение yield выглядит бесполезно, но первое впечатление обманчиво. 19 / 41
  • 23. Интерфейс генераторов: send • Метод send возобновляет выполнение генератора и “отправляет” свой аргумент в следующий yield: >>> gen = g() >>> gen.send("foobar") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can't send [...] to a just-started generator • Чтобы инициализировать генератор нужно “отправить” ему None. Функция next делает ровно это: >>> gen = g() >>> next(gen) • Результатом метода send является следующее значение генератора или исключение StopIteration, если такого исключения нет. >>> gen = g() >>> gen.send(None) # ≡ next(gen) >>> gen.send("foobar") Got 'foobar' 42 20 / 41
  • 24. Интерфейс генераторов: throw • Метод throw поднимает переданное исключение в месте, где генератор приостановил исполнение и возвращает следующее значение генератора. >>> def g(): ... try: ... yield 42 ... except Exception as e: ... yield e ... >>> gen = g() >>> next(gen) 42 >>> gen.throw(ValueError, "something is wrong") ValueError('something is wrong',) >>> gen.throw(RuntimeError, "another error") ??? • Если генератор не обработал брошенное в него исключение, то выполнение генератора прекращается и исключение передаётся наверх по стеку вызовов. 21 / 41
  • 25. Интерфейс генераторов: close • Метод close поднимает специальное исключение GeneratorExit в месте, где генератор приостановил исполнение: >>> def g(): ... try: ... yield 42 ... finally: ... print("Done") ... >>> gen = g() >>> next(gen) 42 >>> gen.close() Done • Если всё хорошо, то метод close завершает работу генератора и ничего не возвращает. • Что может пойти не так? Генератор может обработать исключение GeneratorExit и поднять другое исключение. 22 / 41
  • 26. Генераторы ∼ сопрограммы aka coroutines3 • Сопрограмма — это программа, которая может иметь больше одной точки входа, а также поддерживает остановку и продолжение с сохранением состояния. • Звучит как определение генератора наоборот: >>> def grep(pattern): ... print("Looking for {!r}".format(pattern)) ... while True: ... line = yield ... if pattern in line: ... print(line) ... >>> gen = grep("Gotcha!") >>> next(gen) Looking for 'Gotcha!' >>> gen.send("This line doesn't have ... what we're looking for") >>> gen.send("This one does. Gotcha!") This one does. Gotcha! 3 http://dabeaz.com/coroutines 23 / 41
  • 27. Инициализация сопрограмм • Прежде, чем начать работать с сопрограммой, её нужно инициализировать с помощью вызова функции next. • Объявим декоратор coroutine, который скроет эту деталь реализации: >>> def coroutine(g): ... @functools.wraps(g) ... def inner(*args, **kwargs): ... gen = g() ... next(gen) ... return gen ... return inner ... >>> grep = coroutine(grep) >>> gen = grep("Gotcha!") >>> gen.send("One more line for ya!") • Зачем это всё нужно? Ответ в домашнем задании. 24 / 41
  • 28. Генераторы ∼ легкие потоки aka green threads4 Не сейчас 4 http://dabeaz.com/coroutines 25 / 41
  • 29. Оператор yield from5 • Оператор yield from позволяет делегировать выполнение другому генератору: def chain(*iterables): for iterable in iterables: yield from iterable • Любые вызовы методов send и throw у родительского генератора будут переданы вложенному генератору без изменений. 5 http://python.org/dev/peps/pep-0380 26 / 41
  • 30. Оператор return и исключение StopIteration • Кроме оператора yield в теле генератора можно использовать оператор return. • На человеческом языке использование return означает: «У меня больше нет элементов, извини, возьми лучше вот это.» >>> def g(): ... yield 42 ... return [] # держи! ... >>> gen = g() >>> next(gen) 42 >>> next(gen) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration: [] # вот и оно. 27 / 41
  • 31. return ≡ raise StopIteration • Несмотря на схожесть, использование оператора return в генераторе не эквивалентно поднятию исключения StopIteration. • Контрпример: def g(): try: yield 42 raise StopIteration([]) # ≡ return [] except Exception as e: pass 28 / 41
  • 32. Выражение yield from • Оператор yield from, как и оператор yield, можно использовать в качестве выражения. • При этом значением выражения yield from будет значение атрибута value у поднятого вложенным генератором исключения StopIteration: >>> def f(): ... yield 42 ... return [] ... >>> def g(): ... res = yield from f() ... print("Got {!r}".format(res)) ... >>> gen = g() >>> next(gen) 42 >>> next(gen, None) Got [] 29 / 41
  • 33. Менеджеры контекста и генераторы: мотивация • Протокол менеджеров контекста требует реализации двух методов: __enter__ и __exit__, • Если мы хотим, чтобы у менеджера было какое-то состояние, то мы вынуждены также добавить метод __init__. • В итоге получаем: class cd: def __init__(self, path): self.path = path def __enter__(self): self.saved_cwd = os.getcwd() os.chdir(self.path) def __exit__(self, *exc_info): os.chdir(self.saved_cwd) 30 / 41
  • 34. Менеджеры контекста и генераторы: @contextmanager • Декоратор contextmanager из модуля contextlib принимает генератор специального вида и строит по нему менеджер контекста. from contextlib import contextmanager @contextmanager def cd(path): # __init__ old_path = os.getcwd() # __enter__ os.chdir(path) try: yield # --------- finally: os.chdir(old_path) # __exit__ • Генераторы позволяют сократить количество синтаксического шума при реализации менеджеров контекста. 31 / 41
  • 35. Ещё один пример использования @contextmanager Метод __enter__, построенный декоратором contextmanager, возвращает аргумент оператора yield: import tempfile import shutil @contextmanager def tempdir(): # __init__ outdir = tempfile.mkdtemp() # __enter__ try: yield outdir # --------- finally: shutil.rmtree(outdir) # __exit__ with tempdir() as path: print(path) # ==> /tmp/tmpvfzsmvsv 32 / 41
  • 36. Генераторы: резюме • Генератор в Python — это функция, которая использует операторы yield или yield from. • В мире Python генераторы вездесущи не менее, чем любимые всеми декораторы. • Мы поговорили о том, что генераторы можно использовать • как итераторы, • как сопрограммы, • как легкие потоки, • для компактной реализации менеджеров контекста. 33 / 41
  • 38. Модуль itertools: islice • Функция islice обобщает понятие слайса на произвольный итератор: >>> from itertools import islice >>> xs = range(10) >>> list(islice(xs, 3)) # ≡ xs[:3] [0, 1, 2] >>> list(islice(xs, 3, None)) # ≡ xs[3:] [3, 4, 5, 6, 7, 8, 9] >>> list(islice(xs, 3, 8, 2)) # ≡ xs[3:8:2] [3, 5, 7] • Прелесть функций из модуля itertools в том, что с помощью них легко выражаются самые разнообразные операции над последовательностями. Вопрос Как будет выглядеть функция drop, “выкидывающая” префикс длины n из переданного ей итератора? 34 / 41
  • 39. Модуль itertools: бесконечные итераторы • Для удобства реализуем родственника функции drop: функцию take, которая строит список из более, чем n первых элементов переданного ей итератора. >>> def take(n, iterable): ... return list(islice(iterable, n)) ... >>> list(take(range(10), 3)) [0, 1 2] • Названия бесконечных итераторов говорят сами за себя: >>> from itertools import count, cycle, repeat >>> take(3, count(0, 5)) [0, 5, 10] >>> take(3, cycle([1, 2, 3])) [1, 2, 3] >>> take(3, repeat(42)) [42, 42, 42] >>> take(3, repeat(42, 2)) # не совсем ∞ [42, 42] 35 / 41
  • 40. Модуль itertools: dropwhile и takewhile • Функции dropwhile и takewhile обобщают логику функций drop и take на произвольный предикат. • Обратите внимание, что обе функции возвращают итератор, а не список, как реализованная нами функция take: >>> from itertools import dropwhile, takewhile >>> list(dropwhile(lambda x: x < 5, range(10))) [5, 6, 7, 8, 9] >>> it = takewhile(lambda x: x < 5, range(10)) >>> it <itertools.takewhile object at 0x1022ca748> >>> list(it) [0, 1, 2, 3, 4] 36 / 41
  • 41. Модуль itertools: chain • В модуле itertools реализован уже знакомый нам генератор chain, который конкатенирует произвольное число итераторов: >>> from itertools import chain >>> take(5, chain(range(2), range(5, 10))) [0, 1, 5, 6, 7] • Сконкатенировать итератор итераторов (!) можно с помощью метода chain.from_iterable: >>> it = (range(x, x ** x) for x in range(2, 4)) >>> take(5, chain.from_iterable(it)) [2, 3, 3, 4, 5] Вопрос Чем chain.from_iterable(it) отличается от chain(*it)? 37 / 41
  • 42. Модуль itertools: tee • Функция tee создаёт n независимых копий переданного ей итератора: >>> from itertools import tee >>> it = range(3) >>> a, b, c = tee(it, 3) >>> list(a), list(b), list(c) ([0, 1, 2], [0, 1, 2], [0, 1, 2]) • Использовать it после копирования не рекомендуется, потому что в этом случае скопированные итераторы a, b, c могут пропустить элемент: >>> it = iter(range(3)) >>> a, b = tee(it, 2) >>> used = list(it) >>> list(a), list(b) ([], []) Вопрос Что изменится, если убрать вызов функции iter из второго примера? 38 / 41
  • 43. Модуль itertools: комбинаторные итераторы В модуле itertools в виде итераторов реализованы полезные комбинаторные операции, например: • декартово произведение итераторов, >>> list(itertools.product("AB", repeat=2)) [('A', 'A'), ('A', 'B'), ('B', 'A'), ('B', 'B')] >>> list(itertools.product("AB", repeat=3)) [('A', 'A', 'A'), ('A', 'A', 'B'), ('A', 'B', 'A'), ...] • перестановки элементов итератора, >>> list(itertools.permutations("AB")) [('A', 'B'), ('B', 'A')] • сочетания (с повторениями и без) из элементов итератора. >>> from itertools import combinations, ... combinations_with_replacement >>> list(combinations("ABC", 2)) [('A', 'B'), ('A', 'C'), ('B', 'C')] >>> list(combinations_with_replacement("ABC", 2)) [('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')] 39 / 41
  • 44. Комбинаторные итераторы и функция build_graph def build_graph(words, mismatch_percent): g = ... n_words = len(words) for u, v in itertools.combinations(range(n_words), 2): if len(words[u]) != len(words[v]): continue distance = hamming(words[u], words[v]) # ... return g Вопрос Выглядит неплохо, но можно лучше. Как? 40 / 41
  • 45. Модуль itertools: резюме • Модуль itertools предоставляет обширный набор компонент для реализации операций над последовательностями. • Мы обсудили: • islice, • бесконечные итераторы count, cycle, repeat, • chain, • tee, • комбинаторные итераторы product, permutations, combinations и combinations_with_replacement. 41 / 41