Предположим, у нас имеется вот такая функция:
def getAllAverage(N):
avs = []
count = 0
S = 0
for i in range(1,N+1):
count += 1
S += i
avs.append( S/count )
return avs
которая вычисляет
средние арифметические для каждой из сумм: для одного значения, для двух, трех
и так до N. При вызове
этой функции:
print( getAllAverage(100) )
получим довольно
внушительный список из чисел. В памяти он занимает:
print(getAllAverage(100).__sizeof__() )
888 байт. Конечно,
это относительно немного. Но представьте, что будет, если N увеличить в
десятки раз. Мы получим список, занимающий мегабайты памяти компьютера! Можно
ли здесь как то оптимизировать этот процесс по размеру занимаемой памяти? Да,
для подобных задач в Python предусмотрены так называемые функции-генераторы.
Давайте для начала запишем простую функцию-генератор, а потом вернемся к нашей
исходной задаче.
Пусть имеется
функция, возвращающая обычный список из чисел:
def f():
return list(range(10))
print( f() )
Превратим ее в
функцию-генератор:
def f():
for x in range(10):
yield x
Смотрите, вот
этот оператор yield возвращает
значение x и замораживает
текущее состояние функции. В результате, при ее повторном вызове цикл for начнется не с
начала, а с той итерации, на которой был заморожен, то есть, перейдет к
следующему значению x и оператор yield возвратит
следующее значение.
Для вызова
функции-генератора присвоим ее текущее состояние некоторой переменной:
Эта переменная
будет ссылкой на генератор, то есть, по сути, являться итератором:
и мы уже к ней
можем применить функцию next для перебора значений, которые будет возвращать
функция:
print( next(s) )
print( next(s) )
print( next(s) )
Причем, мы здесь
сразу получаем все преимущества итераторов, а именно – экономию занимаемой
памяти данными.
Теперь вернемся
к нашей исходной задаче и превратим нашу первую функцию в генератор:
def getAllAverage(N):
count = 0
S = 0
for i in range(1,N+1):
count += 1
S += i
yield S/count
и вызовем ее:
it = getAllAverage(10)
print( next(it) )
print( next(it) )
print( next(it) )
print( next(it) )
print( next(it) )
В результате, мы
последовательно будем получать вычисленные значения, не затрачивая
дополнительной памяти на их хранение. Вот в этом и заключается преимущество
таких функций с оператором yield.
Задание для самоподготовки
Пусть дан текст:
t = """Генератор – это
итератор, элементы которого
можно
перебирать (итерировать) только один раз.
Итератор
– это объект, который поддерживает функцию next()
для перехода к
следующему элементу коллекции."""
Написать функцию-генератор для выделения слов из
этого текста (слова разделяются пробелом, либо переносом строки ‘\n’).
Список всех слов при этом в функции не создавать.