Esiste un'espressione per un generatore infinito?


119

Esiste un'espressione generatrice diretta che può produrre infiniti elementi?

Questa è una domanda puramente teorica. Non c'è bisogno di una risposta "pratica" qui :)


Ad esempio, è facile creare un generatore finito:

my_gen = (0 for i in xrange(42))

Tuttavia, per crearne uno infinito ho bisogno di "inquinare" il mio spazio dei nomi con una funzione fasulla:

def _my_gen():
    while True:
        yield 0
my_gen = _my_gen()

Fare le cose in un file separato e in importseguito non conta.


So anche che itertools.repeatfa esattamente questo. Sono curioso di sapere se esiste una soluzione a una riga senza di essa.


9
In realtà non è necessario inquinare il tuo spazio dei nomi ... basta nominare la funzione my_gene poi farlo my_gen = my_gen().
6502

2
puoi anche usare del _my_gense non vuoi confondere i due
John La Rooy

Risposte:


133
for x in iter(int, 1): pass
  • Due argomenti iter= chiamabile senza argomenti + valore sentinella
  • int() ritorna sempre 0

Pertanto, iter(int, 1)è un iteratore infinito. Ci sono ovviamente un numero enorme di variazioni su questo particolare tema (specialmente una volta aggiunto lambdaal mix). Una variante di particolare nota è iter(f, object())che l'utilizzo di un oggetto appena creato come valore sentinella garantisce quasi un iteratore infinito indipendentemente dal chiamabile utilizzato come primo argomento.


3
modo molto interessante da usare itercon proprietà di intcui molte volte dimentichiamo.
Senthil Kumaran

3
puoi usare questa ricetta magica per simulare itertools.count:count = lambda start=0, step=1: (start + i*step for i, _ in enumerate(iter(int, 1)))
Coffee_Table

1
Giusto per spiegare cosa sta succedendo qui: Quando la iter-funzione viene chiamato con due argomenti, si comporta un po 'diverso da quello normalmente: iter(callable, sentinel) -> iterator. L'argomento 1 callableviene chiamato per ogni iterazione dell'iteratore, finché non restituisce il valore di sentinel. Tuttavia, come int()sempre tornare 0, siamo in grado di chiamare int()per sempre e non raggiungerà mai 1. Ciò a effetto produce una lista infinita di 0's
Olsgaard

217

itertools fornisce tre generatori infiniti:

Non conosco nessun altro nella libreria standard.


Dato che hai chiesto una battuta:

__import__("itertools").count()

18
Re: repeat (x, times = ∞) - non c'è nessun simbolo per chi se lo è chiesto - omettere l'argomento fa ripetere per sempre
Mr_and_Mrs_D

Voto positivo perché (sebbene la risposta di ncoghlan affronti direttamente la domanda del PO) questo è più generalmente applicabile.
Huw Walters

Questo è molto più leggibile iter(int, 1)dell'incantesimo. Peccato itertoolsche non abbia un endlessly()metodo il cui unico scopo sia farlo; itertools.count()non è neanche tutto leggibile.
Ballpoint

19

è possibile iterare su un richiamabile restituendo una costante sempre diversa dalla sentinella di iter ()

g1=iter(lambda:0, 1)

8
Lo amo e lo odio entrambi ... Mi piace che realizzi ciò che voglio in così pochi personaggi, ma odio il modo in cui nessuno lo guarderà e saprà cosa dovrebbe fare.
ArtOfWarfare

1
conoscendo la sintassi di iter(qui con sentinella in più) e la sintassi di lambda(qui senza parametri passati, solo return 0), l'unico posto da odiare è quello enigmatico g1.
Sławomir Lenart

@ Gli uomini di SławomirLenart non lo capiscono mai. è solo che era imbarazzantemente piccolo, quindi ho spruzzato 1 g.
user237419

8

Il tuo sistema operativo potrebbe fornire qualcosa che può essere utilizzato come generatore infinito. Ad esempio su Linux

for i in (0 for x in open('/dev/urandom')):
    print i

ovviamente questo non è efficiente come

for i in __import__('itertools').repeat(0)
    print i

11
La soluzione / dev / urandom dipende dalle \ns che appaiono di tanto in tanto ... Devious! :)
hugomg

5

Nessuno che non utilizzi internamente un altro iteratore infinito definito come classe / funzione / generatore (non -expression, una funzione con yield). Un'espressione generatore attinge sempre da un oggetto iterabile e non fa altro che filtrare e mappare i suoi elementi. Non puoi passare da elementi finiti a infiniti con solo mape filter, di cui hai bisogno while(o un forche non termina, che è esattamente ciò che non possiamo avere usando solo fore finiti iteratori).

Curiosità: PEP 3142 è superficialmente simile, ma a un esame più attento sembra che richieda ancora la forclausola (quindi no (0 while True)per te), ovvero fornisce solo una scorciatoia per itertools.takewhile.


Come sospettavo ... Possiamo quindi essere sicuri che non ci sia un generatore infinito prontamente disponibile da abusare? (Purtroppo, xrange (0,1, -1) non funziona ...)
hugomg

2
@missingno: from itertools import repeat, count, cycleprobabilmente conta come "prontamente disponibile" per la maggior parte delle persone.
ncoghlan

1
Oops, ho dimenticato i 2 argomenti iter. Gli iteratori infiniti sono effettivamente disponibili come incorporati - vedi la mia risposta :)
ncoghlan

5

Abbastanza brutto e folle (molto divertente comunque), ma puoi costruire il tuo iteratore da un'espressione usando alcuni trucchi (senza "inquinare" il tuo spazio dei nomi come richiesto):

{ print("Hello world") for _ in
    (lambda o: setattr(o, '__iter__', lambda x:x)
            or setattr(o, '__next__', lambda x:True)
            or o)
    (type("EvilIterator", (object,), {}))() } 

Chiaramente ami LISP
Faissaloo il

1
@Faissaloo Indeed ... Potresti trovare un'espressione ancora più folle su una vecchia pagina che ho scritto: baruchel.github.io/python/2018/06/20/…
Thomas Baruchel

2

Forse potresti usare decoratori come questo, ad esempio:

def generator(first):
    def wrap(func):
        def seq():
            x = first
            while True:
                yield x
                x = func(x)
        return seq
    return wrap

Utilizzo (1):

@generator(0)
def blah(x):
    return x + 1

for i in blah():
    print i

Utilizzo (2)

for i in generator(0)(lambda x: x + 1)():
    print i

Penso che potrebbe essere ulteriormente migliorato per sbarazzarsi di quelli brutti (). Tuttavia dipende dalla complessità della sequenza che si desidera poter creare. In generale, se la tua sequenza può essere espressa utilizzando le funzioni, tutta la complessità e lo zucchero sintattico dei generatori possono essere nascosti all'interno di un decoratore o di una funzione simile a un decoratore.


9
OP chiede un oneliner e tu presenti un decoratore di 10 righe con triplo annidamento defe chiusura? ;)

2
@delnan Beh, ma se definisci il decoratore una volta, puoi avere le tue battute, vero? A quanto ho capito, lo scopo è di avere ogni generatore infinito aggiuntivo implementato in una riga. E questo è ciò che viene presentato qui. Puoi avere (2^x), puoi avere (x). Se migliori un po 'forse anche fibonacci, ecc.
julx

Non risponde alla mia domanda, ma come puoi non amare tutte quelle chiusure soffici? A proposito, sono abbastanza sicuro che puoi sbarazzarti delle parentesi extra eliminando seqe riportando il codice direttamente awrap
hugomg
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.