Come si crea un generatore di ripetizioni, come xrange, in Python? Per esempio, se lo faccio:Come creare un generatore di ripetizioni in Python
>>> m = xrange(5)
>>> print list(m)
>>> print list(m)
Ottengo lo stesso risultato entrambe le volte - i numeri 0..4. Tuttavia, se provo la stessa cosa con rendimento:
>>> def myxrange(n):
... i = 0
... while i < n:
... yield i
... i += 1
>>> m = myxrange(5)
>>> print list(m)
>>> print list(m)
La seconda volta che provo a iterare m, non ottengo niente di nuovo - una lista vuota.
C'è un modo semplice per creare un generatore di ripetizioni come xrange con rendimento o comprensione del generatore? Ho trovato a workaround on a Python tracker issue, che utilizza un decoratore per trasformare un generatore in un iteratore. Questo si riavvia ogni volta che lo usi, anche se non hai usato tutti i valori l'ultima volta, proprio come xrange. Ho anche venuto con il mio decoratore, sulla base della stessa idea, che in realtà restituisce un generatore, ma uno che può riavviare dopo aver lanciato un'eccezione 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()
C'è un modo migliore per risolvere il problema, utilizzando solo rendimento e/o comprensione del generatore? O qualcosa di costruito in Python? Quindi non ho bisogno di rotolare le mie classi e decoratori?
Aggiornamento
Il comment by u0b34a0f6ae inchiodato la fonte della mia equivoco:
xrange (5) non restituisce un iteratore, si crea un oggetto xrange. Gli oggetti xrange possono essere ripetuti, proprio come i dizionari, più di una volta.
La mia funzione "eterna" abbaiava contro l'albero sbagliato tutto, agendo come un iteratore/generatore (__iter__
restituisce auto), piuttosto che come una collezione/xrange (__iter__
restituisce un nuovo iteratore).
Piccola nitpick, ma 'xrange()' non è un generatore. 'type (xrange (4))'! = 'type (myxrange (4))'. –
Penso che sia più di un piccolo nitpick. Questa è l'intera ragione della differenza. E come John ha sottolineato, il comportamento desiderato può essere ottenuto con un __iter__ sovraccarico. – ricree
Ho molti problemi a seguire il codice di implementazione, rispetto alle altre due implementazioni proposte (una nel problema del tracker Python a cui ti sei collegato, l'altra nella risposta di @ JohnMillikin). In particolare, avendo difficoltà a capire: (1) cosa significa esattamente "@ decorator.decorator". Puoi dare un link a doc per questo? (2) un esempio di utilizzo sarebbe molto utile; in particolare, uno che esercita args e nargs (3) e puoi dare un esempio di come la tua gestione di StopIteration aggiunge valore? ovvero un esempio in cui la vostra implementazione ha esito positivo, ma le altre due implementazioni falliscono. –