porton
porton

Reputation: 5805

"Double" iterator and a generator function

I want to get "next asset" with an iterator-like object, but (instead of __next__() method) there are two algorithms loading next asset (next1 and next2 below), which can be implemented as a "quasi-iterator" like:

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

    def next1(self):
        # ...

    def next2(self):
        # ...

To be clear, what is the next retrieved object may depends on the "history" of calling next1 and next2, like:

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

My question: May this (two kinds of "next" step in an iterator) be implemented as a generator function?

I guess this can be done with a global variable to which the function refers. But can it be done without using global variables, but with some local variable?

If it is hard or impossible with current Python, can we discuss how to add new semantics to Python to make it possible?

Upvotes: 1

Views: 167

Answers (2)

PM 2Ring
PM 2Ring

Reputation: 55469

Here's a simple example of using send to switch a generator between two different iteration modes: it either increments its current value or multiplies it. The same principle can be applied to your graph traversal task.

The send method allows you to send an object into the generator. Annoyingly, the result of send is the current value that you would have obtained by calling next; it would be nice if you could send without having the generator yield a value, but that's just something we have to live with.

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))       

output

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

If you have trouble applying this technique to your graph traversal task please ask a fresh question (possibly linking to this one) that includes some relevant graph code, so that answerers don't have to write that stuff from scratch in order to test and demonstrate their code.

Upvotes: 3

Yassine Faris
Yassine Faris

Reputation: 991

You can try this:

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

Upvotes: 1

Related Questions