Python comment consommer partiellement un générateur itérable (sans `next`)?

Nov 15 2020

J'ai une classe agissant comme un générateur itérable (selon la meilleure façon de recevoir la valeur de «retour» d'un générateur python ) et je souhaite la consommer partiellement avec des forboucles.

Je ne peux pas utiliser next(comme Python - consommer un générateur à l'intérieur de divers consommateurs ) car la première consommation partielle utilise une bibliothèque qui ne prend que des itérateurs. Comment puis-je continuer à utiliser le générateur à partir de là où la fonction de bibliothèque s'est arrêtée?

(En relation: Pause Python Generator , existe-t-il un moyen de `` mettre en pause '' ou de consommer partiellement un générateur en Python, puis de reprendre la consommation plus tard là où elle est laissée? )

class gen(): # https://stackoverflow.com/q/34073370
    def __iter__(self):
        for i in range(20):
            yield i

# I want to partially consume in the first loop and finish in the second
my_gen_2 = gen()
for i in my_gen_2:  # imagine this is the internal implementation of the library function
    print(i)
    if i > 10:      # the real break condition is when iterfzf recieves user input
        break

for i in my_gen_2:  # i'd like to process the remaining elements instead of starting over
    print('p2', i)

# the confusion boils down to this
my_gen = gen()
for i in my_gen:
    print(i)    # prints 1 through 20 as expected
for i in my_gen:
    print('part two', i)    # prints something, even though the generator should have been "consumed"?

Réponses

2 napuzba Nov 15 2020 at 02:36

Chaque fois que vous faites un générateur itératif dans une boucle, vous obtenez un nouvel itérateur. Par exemple:

class gen(): # https://stackoverflow.com/q/34073370
    def __init__(self):
        self.count = 0
    def __iter__(self):
        self.count += 1
        print("Hallo iter {0}".format(self.count))
        yield self.count


my_gen = gen()
>>> for i in my_gen:
...    pass
Hallo iter 1
>>> for i in my_gen:
...    pass
Hallo iter 2

Si vous souhaitez utiliser l'ancien itérateur, travaillez simplement avec gen().__iter__()

>>> my_gen = gen().__iter__()
>>> for i in my_gen:
...    pass
Hallo iter 1
>>> for i in my_gen:
...    pass
Exr0n Nov 15 2020 at 02:44

Comme @napuzba l'a souligné, __iter__renvoie un tout nouveau générateur à chaque fois qu'il est utilisé. Au lieu de cela, stockez l'état dans self:

class gen():
    def __init__(self):
        self.count = 0
    def __iter__(self):
        while self.count < 20:
            self.count += 1
            yield self.count