«Двойной» итератор и функция генератора - PullRequest
0 голосов
/ 30 апреля 2018

Я хочу получить «следующий актив» с помощью объекта, похожего на итератор, но (вместо метода __next__()) есть два алгоритма загрузки следующего актива (next1 и next2 ниже), которые могут быть реализованы как "квази-итератор", такой как:

class AssetLoader(object):
    def __init___(self):
        pass

    def next1(self):
        # ...

    def next2(self):
        # ...

Чтобы было понятно, какой следующий извлеченный объект может зависеть от "истории" вызовов next1 и next2, например:

next1(); next1(); next2(); next1(); next2()

Мой вопрос: можно ли реализовать это (два вида «следующего» шага в итераторе) как функцию генератора?

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

Если это сложно или невозможно с текущим Python, можем ли мы обсудить, как добавить новую семантику в Python, чтобы сделать это возможным?

Ответы [ 2 ]

0 голосов
/ 30 апреля 2018

Вот простой пример использования send для переключения генератора между двумя различными режимами итерации: он либо увеличивает свое текущее значение, либо умножает его. Тот же принцип может быть применен к вашей задаче обхода графа.

Метод send позволяет отправить объект в генератор. Досадно, что результат send - это текущее значение, которое вы получили бы, вызвав next; было бы неплохо, если бы вы могли отправить, не имея генератора, выдающего значение, но это просто то, с чем мы должны жить.

def add_or_mul(current, step, scale, mode='add'):
    ''' A generator that either adds step to the current value,
        or multiplies it by scale
    '''
    while True:
        newmode = yield current
        if newmode is not None:
            if newmode not in ('add', 'mul'):
                raise ValueError('Bad mode: ' + newmode)
            mode = newmode
        if mode == 'add':
            current += step
        else:
            current *= scale

# Test

    gen = add_or_mul(1, 1, 2)
    for i in range(5):
        print(next(gen))
    print(gen.send('mul'))
    for i in range(4):
        print(next(gen))
    print(gen.send('add'))
    for i in range(4):
        print(next(gen))       

выход

    1
    2
    3
    4
    5
    10
    20
    40
    80
    160
    161
    162
    163
    164
    165

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

0 голосов
/ 30 апреля 2018

Вы можете попробовать это:

class AssetLoader(object):
    def __init___(self):
        self.current_next = self.next1

    def next1(self):
        if condition:
            self.current_next = self.next2
        elif conition:
            return x
        else:
            raise StopIteration

    def next2(self):
        if condition:
            self.current_next = self.next1
        elif conition:
            return y
        else:
            raise StopIteration

    def __next__(self):
        return self.current_next()

    def __iter__(self):
        return self
...