Комментарии 15
не стоит путать «генераторы коллекций» (comprehensions, они же «включения»)
Смущает терминология: что же здесь всё-таки имелось в виду? generator expressions или list comprehensions?
Это уже третья сущность :)
Смущает терминология: что же здесь всё-таки имелось в виду? generator expressions или list comprehensions?
Не очень понял, что тут смутило: генераторы коллекций — это генераторы списков (list comprehensions) + генераторы множеств + генераторы словарей.
Слова с корнем "генератор" перегружены разными смыслами. Не случайно в англоязычной литературе пишут "list comprehension", а не "list generator".
Допустим у нас есть вот такой класс:
class MyIterable:
def __init__(this):
this.value = 0
def __iter__(this):
return this
def __next__(this):
this.value += 1
print("Current value", this.value)
if this.value < 5:
return this.value
else:
raise StopIteration
Затем используем его в list comprehension:
>>> l = [a for a in MyIterable()]
>>> print(l)
Current value 1
Current value 2
Current value 3
Current value 4
Current value 5
[1, 2, 3, 4]
>>> for i in l:
>>> print(i)
1
2
3
4
А теперь поиграемся в генераторы
>>> g = (a for a in MyIterable())
>>> print(g)
<generator object <genexpr> at 0x7f341679dc10>
>>> for i in g:
>>> print(i)
Current value 1
1
Current value 2
2
Current value 3
3
Current value 4
4
Current value 5
Мы получили генератор, но не использвали yield.
Ну, и вообще термин "генератор-итератор" меня, мягко говоря, вводит в ступор. Что уж говорить о самых маленьких :) Я-то думал, что Generator — это одно, а Iterator — это немножко другое.
генератор — частный случай итератора
Не совсем так. Генератор — это функция, которая возвращает итератор. В свою очередь итератор — это не функция, а объект, который семантически представляет собой поток данных и имплементирует итераторный протокол (т.е. имеет методы __iter__()
и, самое главное, __next__()
).
Действительно, есть ещё "generator iterator". Но это не генератор-итератор, а скорее генераторный итератор по-русски, т.е. итератор изготовленный генератором. В тексте статьи он назван generator object. Видимо потому, что такое название выдаёт его метод __str__()
.
Вообще, перед тем, как рассказать маленьким об итераторах и генераторах, лучше начать с ленивых (отложенных) вычислений и санок (thunks). Потому как итераторы — это именно что синтаксический сахар, и аналогичный эффект может быть достигнут и без yield
, и без генераторных выражений. Мне этим питон не нравится в качестве языка для обучения CS — скрывает фундаментальные вещи за сахаром.
Нормальный путь питониста вглубь этой темы:
Не знать про yield. Спокойно в своём уютненьком юпитер-нотбуке совать пандас в матплотлиб и горя не знать.
Вау, оказывается, можно не один раз отдавать результат из функции, а несколько раз. Или вообще много раз. Или, хе-хе, бесконечность раз. Прикол. Непонятно, правда, зачем оно может быть нужно.
Разобрались зачем это нужно, вошли во вкус, пользуемся. Возможность выстраивать логику программы не от источника данных, а наоборот, от потребителя - действительно иногда удобно. Да что там говорить, почти всегда удобнее.
Оказывается, yield умеет не только отдавать, но ещё и принимать данные. Ой.
Туго, со скрипом, через боль и фрустрацию въезжаем в тему async/await и внезапно обнаруживаем, что теперь у нас каждая мать её функция является этим самым генератором, который где-то там внутри под капотом елдячит на каждом "await".
... не знаю, я дальше не ходил.
Спасибо, очень понятно написано. Параллель с Тридевятым царством и Вовкой прекрасна!
Генераторы для самых маленьких