Сообщить об ошибке.

Преобразование простой функции в генератор Python

Если есть функция, которая возвращает последовательность, то скорее всего, ее можно превратить в функцию-генератор.

Например есть функция, которая перебирает символы строки, пока не будет достигнуто их заданное количество stop_item, значение stop_item=0 будет возвращать список всех символов строки:

def stop_iter(iteration, stop_item=0):
    elements = []
    for n, item in enumerate(iteration, 1):
        elements.append(item)
        if n == stop_item:
            break
    return elements

Эта функция принимает итерируемый объект (в данном случае строку) и возвращает по очереди элементы этого объекта, включая элемент под номером stop_item:

>>> string = 'абракадабра'
>>> results = stop_iter(string, 7)
>>> results
# ['а', 'б', 'р', 'а', 'к', 'а', 'д']

Представленная выше функция создает итоговый список в момент её вызова, а затем возвращает его. Но что, если генерируемый список очень большой и есть требование, не забивать оперативную память. В этом случае функция должна возвращать "ленивую" итерацию вместо результирующего списка.

Как преобразовать обычную функцию в функцию-генератор?

Как можно вернуть итерируемый объект, пока не начнется перебор элементов в цикле? Для этого необходимо использовать оператор yield. Оператор yield - это то, что превращает функцию в функцию генератора.

Момент вызовов добавления нового элемента elements.append(item) в конец итогового списка elements необходимо заменить оператором yield item, а затем полностью удалить из функции упоминание об списке elements:

Для более ясного понимания, какие преобразования выполнены, строки, которые необходимо удалить оставим закомментированными.

def stop_iter(iteration, stop_item=0):
    # elements = []
    for n, item in enumerate(iteration, 1):
        # elements.append(item)
        yield item
        if n == stop_item:
            break
    # return elements

Теперь, если вызвать функцию stop_iter(), она на самом деле не запускает код, расположенный внутри неё. Вместо этого функция stop_iter() возвращает объект-генератор, который будет работать, пока его перебирают.

>>> string = 'абракадабра'
>>> results = stop_iter(string, 7)
>>> results
# <generator object stop_iter at 0x7f1508dced60>
>>> next(results)
# 'а'
>>> next(results)
# 'б'
>>> list(results)
# ['р', 'а', 'к', 'а', 'д']

# не забываем, что генераторы конечны.
>>> list(results)
# []
>>> next(results)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# StopIteration