я не ставлю задачи рассказать слушателям о практических вещах типа заморозки функции при помощи yield, но хотел бы показать что представляют из себя генераторы и итераторы под капотом(не углубляясь сильно).
итак, вот мои тезисы:
- 1. следует чётко различать понятия: итерируемый объект, итератор; функция-генератор, генератор.
2. итератор и генератор реализуют интерфейс итератора. То есть имеют методы __iter__(), __next__().
3. итерируемый объект и функция генератор обязательно имеют функцию __iter__(), которая возвращает итератор и генератор соответственно.
4. итерируемый объект содержит коллекцию, к элементам которой можно обратиться по индексу.
5. функция-генератор содержит “формулу”, по которой генерируются элементы коллекции.
6. итерируемый объект можно перебирать при помощи итератора много раз.
7. функцию-генератор можно перебрать при помощи генератора только один раз.
8. генератор в отличие от итератора экономит оперативную память потому что не хранит всю коллекцию в ней.
9. если генератору вместо “формулы” передать коллекцию, то он будет перебирать её как итератор(не экономя оперативную память).
10. каждая итериция в итераторе и в генераторе совершается явным вызовом __next__().
11. цикл for при каждом проходе неявно вызывает __next__().
12. генераторное выражение является синтаксическим сахаром для функции-генератора. оно тоже имеет “формулу” и __iter__().
Пример итератора:
class Obj(): # Итерируемый объект def __init__(self, word): self.word = word def __iter__(self): return Iterator(self.word) class Iterator: # Итератор def __init__(self, word): self.word = word self.index = 0 def __next__(self): try: letter = self.word[self.index] self.index += 1 return letter except IndexError: raise StopIteration() def __iter__(self): return self obj = Obj('sergey') it = iter(obj) print(it.__next__()) print(it.__next__()) print(it.__next__()) print(it.__next__()) print(it.__next__()) print(it.__next__()) print(it.__iter__())
Пример генератора:
class Obj(): # функция-генератор def __init__(self, word): self.word = word def __iter__(self): # генератор for l in self.word: yield l obj = Obj('sergey') it = iter(obj) print(next(it)) print(next(it)) print(next(it)) print(next(it)) print(next(it)) print(next(it)) print(iter(it))