Раньше мы видели, как функции обратного вызова (колбеки) могут быть лучше простых функций, но есть и другие варианты. Мы можем создать, вероятно, неограниченный итератор, который будет обходить элементы нашей последовательности, делая код еще более понятным, чем даже решение с колбеками.

Решение с колбеком

Просто для напоминания - вот наше решение с колбеком. У нас есть функция fibonacci, которая проходит элементы последовательности, и для каждого элемента вызывает переданную функцию check_17.

examples/python/fibonacci_function_callback.py

#!/usr/bin/env python
from __future__ import print_function

def fibonacci(cb):
    values = []
    while(True):
        if len(values) < 2:
            values.append(1)
        else:
            values = [values[-1], values[-1] + values[-2]]

        r = cb(values[-1])
        if (r[0]):
            return(r[1])

def check_17(v):
    if v % 17 == 0:
        return (True, v)

    if v > 10000:
        return (True, None)

    return (False,)


if __name__ == '__main__':
    res = fibonacci(check_17)
    if (res != None):
        print(res)

Тот факт, что мы должны иметь возможность передать функции fibonacci сигнал, когда остановиться, делает наш код несколько сложнее, чем мы надеялись. Мы должны возвращать массив, в котором первый элемент выполняет роль индикатора (True/False).

Создание Fibonacci-итератора

Давайте сделаем все наоборот и позволим пользователю вернуть контроль над циклом. Мы создаем класс Fibonacci, который будет итерабельным вследствие добавления метода __iter__, который просто возвращает объект, и метода next (В Python 3, думаю, это будет __next__), который возвращает следующий элемент.

Внутри объект содержит текущее состояние итерации. В нашем случае это значит, что он должен содержать последние два элемента последовательности.

examples/python/fibonacci_iterator.py

#!/usr/bin/env python
from __future__ import print_function

class Fibonacci(object):
    def __init__(self):
        self.values = []

    def __iter__(self):
        return self

    def next(self):
        if len(self.values) < 2:
            self.values.append(1)
        else:
            self.values = [self.values[-1], self.values[-1] + self.values[-2]]
        return self.values[-1]

for f in Fibonacci():
    if f % 17 == 0:
        print(f)
        break
    if f > 10000:
        break

Вызов fib = Fibonacci() создаст объект итератора, который мы можем использовать в конструкции for in для перебора элементов. Поскольку это неограниченный итератор, то есть, он не имеет конца, мы должны быть уверены, что есть какой-то код внутри цикла for, который его как-то остановит.

Решение выглядит проще, чем вариант с колбеками.