Пользовательский итератор и проблема itertools.tee - PullRequest
1 голос
/ 09 мая 2019

Мой пользовательский итератор должен вызывать определенный метод при вызове следующего. Сначала он работает таким образом, но после повторного вызова itertools.tee для итератора метод не вызывается.

У меня уже есть решение / обходной путь, но я бы хотел понять причину проблемы.

class MyIterator(object):
    def __init__(self, elements):
        self._elements = iter(elements)

    def __iter__(self):
        return self

    def next(self):
        element = (self._elements)

        if isinstance(element, HwState):
            element.el_method()

        return element

elements = list(...)
iterator1, iterator2 = itertools.tee(MyIterator(elements))
element1 = next(iterator2)    # ok
element2 = next(iterator2)    # ok
iterator1, iterator2 = itertools.tee(MyIterator(iterator1))
element1 = next(iterator2)    # el_method() is not called but correct element is returned
element2 = next(iterator2)    # el_method() is not called but correct element is returned

Я «решил» проблему следующим образом:

elements = list(...)
iterator = MyIterator(elements)
element1 = next(iterator)
element2 = next(iterator)
iterator = MyIterator(elements)
element1 = next(iterator)    # el_method() is called, correct element is returned
element2 = next(iterator)    # el_method() is called, correct element is returned

1 Ответ

0 голосов
/ 09 мая 2019

См. "Примерно эквивалентную" реализацию itertools.tee, включенную в документацию:

def tee(iterable, n=2):
    it = iter(iterable)
    deques = [collections.deque() for i in range(n)]
    def gen(mydeque):
        while True:
            if not mydeque:             # when the local deque is empty
                try:
                    newval = next(it)   # fetch a new value and
                except StopIteration:
                    return
                for d in deques:        # load it to all the deques
                    d.append(newval)
            yield mydeque.popleft()
    return tuple(gen(d) for d in deques)

По существу, tee сохраняет очередь для каждого сгенерированного итератора. Когда запрашивается новое значение, если в очереди итератора что-то есть, оно получает оттуда следующее значение, и если очередь пуста, он вызывает next в исходном итераторе один раз и добавляет результат в каждую очередь. Это означает, что сгенерированные значения «кэшируются» и возвращаются каждым итератором вместо дублирования работы по созданию элемента.

Более того, tee в общем случае будет вести себя не так, как вы ожидаете, поскольку tee не может знать, как вообще делать копии итератора. Подумайте, например, о текстовом файле. Как только вы прочитаете одну строку в принципе, вы не сможете вернуться назад (в простом последовательном доступе), и не будет такой вещи, как «дублирование файлового итератора» как такового (для эмуляции чего-то подобного вам потребуется несколько обработчиков файлов или поиск), поэтому Вы просто сохраняете прочитанные строки и возвращаете их позже в других итераторах.

...