Comment créer un générateur répétitif, comme xrange, en Python? Par exemple, si je le fais:Comment créer un générateur répétitif en Python
>>> m = xrange(5)
>>> print list(m)
>>> print list(m)
Je reçois le même résultat deux fois - les nombres 0..4. Cependant, si je tente la même chose avec le rendement:
>>> def myxrange(n):
... i = 0
... while i < n:
... yield i
... i += 1
>>> m = myxrange(5)
>>> print list(m)
>>> print list(m)
La deuxième fois que j'essaie de parcourir m, je reçois rien en retour - une liste vide.
Existe-t-il un moyen simple de créer un générateur répétitif comme xrange avec le rendement ou la compréhension des générateurs? J'ai trouvé a workaround on a Python tracker issue, qui utilise un décorateur pour transformer un générateur en un itérateur. Cela redémarre chaque fois que vous commencez à l'utiliser, même si vous n'avez pas utilisé toutes les valeurs la dernière fois, tout comme xrange. Je suis également venu avec mon propre décorateur, basé sur la même idée, qui retourne en fait un générateur, mais qui peut redémarrer après avoir jeté une exception StopIteration:
@decorator.decorator
def eternal(genfunc, *args, **kwargs):
class _iterable:
iter = None
def __iter__(self): return self
def next(self, *nargs, **nkwargs):
self.iter = self.iter or genfunc(*args, **kwargs):
try:
return self.iter.next(*nargs, **nkwargs)
except StopIteration:
self.iter = None
raise
return _iterable()
Est-il une meilleure façon de résoudre le problème, en utilisant seulement des compréhensions de rendement et/ou de générateur? Ou quelque chose construit en Python? Donc je n'ai pas besoin de rouler mes propres classes et décorateurs?
Mise à jour
Le comment by u0b34a0f6ae clouer la source de mon incompréhension:
xrange (5) ne retourne pas iterator, il crée un objet xrange. Les objets xrange peuvent être itérés, tout comme les dictionnaires, plus d'une fois.
Ma fonction « éternelle » aboyait entièrement le mauvais arbre, en agissant comme un itérateur/générateur (__iter__
retourne auto) plutôt que comme une collection/xrange (__iter__
retourne une nouvelle iterator).
Petit pointeur, mais 'xrange()' n'est pas un générateur. 'type (xrange (4))'! = 'type (myxrange (4))'. –
Je pense que c'est plus qu'un petit nitpick. C'est toute la raison de la différence. Et comme John l'a souligné, le comportement souhaité peut être obtenu avec un __iter__ surchargé. – ricree
J'ai beaucoup de mal à suivre votre code d'implémentation, comparé aux deux autres implémentations proposées (l'une dans le problème du tracker Python auquel vous êtes lié, l'autre dans la réponse de @ JohnMillikin). En particulier, avoir du mal à comprendre: (1) ce que signifie exactement "@ decorator.decorator". Pouvez-vous donner un lien vers doc pour cela? (2) un exemple d'utilisation serait très utile; en particulier, celui qui exerce args et nargs (3) et pouvez-vous donner un exemple de la façon dont votre traitement StopIteration ajoute de la valeur? c'est-à-dire un exemple dans lequel votre implémentation réussit mais les deux autres implémentations échouent. –