49 votes

Comment puis-je savoir si un générateur était juste commencé?

J'aimerais une fonction, is_just_started, qui se comporte comme suit:

>>> def gen(): yield 0; yield 1
>>> a = gen()
>>> is_just_started(a) 
True
>>> next(a)
0
>>> is_just_started(a) 
False
>>> next(a)
1
>>> is_just_started(a) 
False
>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> is_just_started(a)
False

Comment puis-je mettre en œuvre cette fonction?

J'ai regardé l' .gi_running d'attribut, mais il semble être utilisé pour autre chose.

Si je sais que la première valeur qui doit être envoyé dans le générateur, je peux faire quelque chose comme ceci:

def safe_send(gen, a):
    try:
        return gen.send(a)
    except TypeError as e:
        if "just-started" in e.args[0]:
            gen.send(None)
            return gen.send(a)
        else:
            raise

Toutefois, cela semble aberrant.

69voto

Tim Tisdall Points 3153

Cela ne fonctionne que dans Python 3.2+:

>>> def gen(): yield 0; yield 1
... 
>>> a = gen()
>>> import inspect
>>> inspect.getgeneratorstate(a)
'GEN_CREATED'
>>> next(a)
0
>>> inspect.getgeneratorstate(a)
'GEN_SUSPENDED'
>>> next(a)
1
>>> inspect.getgeneratorstate(a)
'GEN_SUSPENDED'
>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> inspect.getgeneratorstate(a)
'GEN_CLOSED'

Ainsi, la fonction demandée est:

import inspect

def is_just_started(gen):
    return inspect.getgeneratorstate(gen) == inspect.GEN_CREATED:

Par curiosité, j'ai regardé dans Disponible de comprendre comment il a été la détermination de cette... Apparemment, il regarde generator.gi_frame.f_lasti ce qui est l'indice de la dernière tentative d'instruction bytecode". Si c'est -1 alors qu'il n'a pas encore commencé.

Voici un py2 version:

def is_just_started(gen):
    return gen.gi_frame is not None and gen.gi_frame.f_lasti == -1

26voto

wim Points 35274

Faire un nouveau générateur qui donne simplement à partir de votre générateur d'intérêt. Il définit un indicateur, une fois la première valeur a été consommé. Par la suite, il suffit d'utiliser yield from pour le reste des éléments.

Utiliser le produit de remplacement de générateur en remplacement du générateur qui vous intéresse dans le suivi de la "is_just_started" de l'état.

Cette technique est non-intrusif, et peut même être utilisé sur les générateurs pour lesquels vous n'avez aucun contrôle sur le code source.

5voto

Moinuddin Quadri Points 27539

Vous pouvez créer un itérateur et de définir le drapeau comme la propriété d'instance de classe iterator:

class gen(object):
    def __init__(self, n):
        self.n = n
        self.num, self.nums = 0, []
        self.is_just_started = True  # Your flag

    def __iter__(self):
        return self

    # Python 3 compatibility
    def __next__(self):
        return self.next()

    def next(self):
        self.is_just_started = False  # Reset flag with next
        if self.num < self.n:
            cur, self.num = self.num, self.num+1
            return cur
        else:
            raise StopIteration()

Et votre valeur de la fonction de contrôle serait:

def is_just_started(my_generator):
    return my_generator.is_just_started

Exemple d'exécution:

>>> a = gen(2)

>>> is_just_started(a)
True

>>> next(a)
0
>>> is_just_started(a)
False

>>> next(a)
1
>>> is_just_started(a)
False

>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 19, in next
StopIteration

Pour connaître la différence entre itérateur et générateur, vérifier la Différence entre Python Générateurs et les Itérateurs

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X