Gibt es einen Ausdruck für einen unendlichen Generator?

119

Gibt es einen einfachen Generatorausdruck, der unendliche Elemente ergeben kann?

Dies ist eine rein theoretische Frage. Hier ist keine "praktische" Antwort erforderlich :)


Zum Beispiel ist es einfach, einen endlichen Generator herzustellen:

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

Um jedoch eine unendliche zu erstellen, muss ich meinen Namespace mit einer Scheinfunktion "verschmutzen":

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

Dinge in einer separaten Datei zu tun und importspäter zu tun, zählt nicht.


Ich weiß auch, dass itertools.repeatdas genau das tut. Ich bin gespannt, ob es eine Einzeilerlösung ohne diese gibt.

Hugomg
quelle
9
Sie müssen Ihren Namespace nicht wirklich verschmutzen ... benennen Sie einfach die Funktion my_genund tun Sie es dann my_gen = my_gen().
6502
2
Sie können auch verwenden, del _my_genwenn Sie die beiden nicht verwechseln möchten
John La Rooy

Antworten:

131
for x in iter(int, 1): pass
  • Zwei Argumente iter= null Argumente aufrufbar + Sentinel-Wert
  • int() kehrt immer zurück 0

Daher iter(int, 1)ist ein unendlicher Iterator. Es gibt offensichtlich eine große Anzahl von Variationen dieses speziellen Themas (besonders wenn Sie es lambdain die Mischung aufnehmen). Eine Variante von besonderer Bedeutung ist iter(f, object()), dass die Verwendung eines frisch erstellten Objekts als Sentinel-Wert fast einen unendlichen Iterator garantiert, unabhängig davon, welcher Aufruf als erstes Argument verwendet wird.

ncoghlan
quelle
3
sehr interessante Art, itermit Eigentum zu verwenden, intdas wir oft vergessen.
Senthil Kumaran
3
Sie können dieses magische Rezept verwenden, um zu simulieren itertools.count:count = lambda start=0, step=1: (start + i*step for i, _ in enumerate(iter(int, 1)))
Coffee_Table
1
Nur um zu erklären, was hier vor sich geht: Wenn die iter-Funktion mit zwei Argumenten aufgerufen wird, verhält sie sich etwas anders als normalerweise : iter(callable, sentinel) -> iterator. Argument 1 callablewird für jede Iteration des Iterators aufgerufen, bis der Wert von zurückgegeben wird sentinel. Da wir jedoch int()immer zurückkehren werden 0, können wir für immer anrufen int()und niemals 1 erreichen. Dies wird 0
tatsächlich
214

itertools bietet drei unendliche Generatoren:

Ich kenne keine anderen in der Standardbibliothek.


Da Sie nach einem Einzeiler gefragt haben:

__import__("itertools").count()
Katriel
quelle
18
Betreff: wiederholen (x, times = ∞) - es gibt kein Symbol für jeden, der sich gefragt hat - das Weglassen des Arguments führt zu einer wiederholten Wiederholung
Mr_and_Mrs_D
Upvoted, weil (während die Antwort von ncoghlan direkt auf die Frage des OP eingeht) dies allgemeiner anwendbar ist.
Huw Walters
Dies ist verdammt viel lesbarer als die iter(int, 1)Beschwörung. Schade, dass itertoolses keine endlessly()Methode gibt, deren einziger Zweck dies ist. itertools.count()ist auch nicht so lesbar.
BallpointBen
19

Sie können über eine aufrufbare Datei iterieren und eine Konstante zurückgeben, die sich immer vom Sentinel von iter () unterscheidet

g1=iter(lambda:0, 1)
user237419
quelle
8
Ich liebe und hasse das ... Ich liebe es, dass es in so wenigen Charakteren das erreicht, was ich will, aber ich hasse es, wie niemand es jemals ansehen und wissen wird, was es tun soll.
ArtOfWarfare
1
Wenn man die Syntax von iter(hier mit zusätzlichem Sentinel) und die Syntax von lambda(hier nur ohne übergebene Parameter return 0) kennt , ist der einzige Ort, an dem man hassen kann, dieser rätselhafte g1.
Sławomir Lenart
@ SławomirLenart Männer bekommen es nie. Es ist nur peinlich klein, also habe ich 1 g gespritzt.
user237419
8

Ihr Betriebssystem bietet möglicherweise etwas, das als unendlicher Generator verwendet werden kann. ZB unter Linux

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

offensichtlich ist dies nicht so effizient wie

for i in __import__('itertools').repeat(0)
    print i
John La Rooy
quelle
11
Die / dev / urandom-Lösung hängt davon ab, \nob sie von Zeit zu Zeit erscheint ... Devious! :)
Hugomg
5

Keiner, der intern keinen anderen unendlichen Iterator verwendet, der als Klasse / Funktion / Generator definiert ist (kein Ausdruck, eine Funktion mit yield). Ein Generatorausdruck bezieht sich immer auf einen iterierbaren Anoter und filtert und ordnet seine Elemente nur zu. Sie können nicht mit nur mapund von endlichen zu unendlichen Elementen filterwechseln while(oder einem for, der nicht endet, was genau das ist, was wir nicht nur mit forund mit endlichen Iteratoren haben können).

Wissenswertes: PEP 3142 ist oberflächlich ähnlich, aber bei näherer Betrachtung scheint es, dass es immer noch die forKlausel erfordert (also nein (0 while True)für Sie), dh nur eine Abkürzung für itertools.takewhile.


quelle
Wie ich vermutet habe ... Können wir dann sicher sein, dass es keinen sofort verfügbaren unendlichen Generator gibt, der missbraucht werden kann? (Leider funktioniert xrange (0,1, -1) nicht ...)
hugomg
2
@missingno: from itertools import repeat, count, cyclezählt für die meisten Leute wahrscheinlich als "leicht verfügbar".
Ncoghlan
1
Hoppla, ich habe 2 Argumente vergessen iter. Unendliche Iteratoren sind tatsächlich als eingebaute verfügbar - siehe meine Antwort :)
ncoghlan
5

Ziemlich hässlich und verrückt (allerdings sehr lustig), aber Sie können Ihren eigenen Iterator aus einem Ausdruck erstellen, indem Sie einige Tricks anwenden (ohne Ihren Namespace nach Bedarf zu "verschmutzen"):

{ 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,), {}))() } 
Thomas Baruchel
quelle
Sie lieben eindeutig LISP
Faissaloo
1
@Faissaloo In der Tat ... Vielleicht finden Sie auf einer alten Seite, die ich geschrieben habe, einen noch verrückteren Ausdruck: baruchel.github.io/python/2018/06/20/…
Thomas Baruchel
2

Vielleicht könnten Sie Dekorateure wie diesen verwenden, zum Beispiel:

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

Verwendungszweck 1):

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

for i in blah():
    print i

Verwendung (2)

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

Ich denke, es könnte weiter verbessert werden, diese Hässlichen loszuwerden (). Dies hängt jedoch von der Komplexität der Sequenz ab, die Sie erstellen möchten. Wenn Ihre Sequenz mithilfe von Funktionen ausgedrückt werden kann, kann im Allgemeinen die gesamte Komplexität und der syntaktische Zucker von Generatoren in einem Dekorator oder einer dekoratorähnlichen Funktion verborgen sein.

Julx
quelle
9
OP fragt nach einem Oneliner und Sie präsentieren einen 10-zeiligen Dekorateur mit dreifacher Verschachtelung defund Verschluss? ;)
2
@delnan Nun, aber wenn Sie den Dekorateur einmal definieren, können Sie Ihre Einzeiler haben, nicht wahr? Soweit ich weiß, besteht der Zweck darin, jeden zusätzlichen unendlichen Generator in einer Zeile zu implementieren. Und das wird hier vorgestellt. Sie können haben (2^x), Sie können haben (x). Wenn Sie es ein wenig verbessern, möglicherweise auch Fibonacci usw.
Julx
Beantwortet meine Frage nicht, aber wie kann man dann nicht all diese flauschigen Verschlüsse lieben? Übrigens, ich bin mir ziemlich sicher, dass Sie die zusätzlichen Parens loswerden können, indem Sie seqden Code loswerden und direkt wrap
zurückrücken,