Czy istnieje wyrażenie na nieskończony generator?

119

Czy istnieje proste wyrażenie generatora, które może dać nieskończone elementy?

To jest kwestia czysto teoretyczna. Nie ma tu potrzeby "praktycznej" odpowiedzi :)


Na przykład łatwo jest utworzyć skończony generator:

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

Jednak aby stworzyć nieskończoną, muszę „zanieczyścić” moją przestrzeń nazw fałszywą funkcją:

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

Robienie rzeczy w osobnym pliku i importpóźniejsze nie liczą się.


Wiem też, że itertools.repeatto właśnie robi. Ciekawi mnie, czy bez tego istnieje jednoliniowe rozwiązanie.

hugomg
źródło
9
W rzeczywistości nie musisz zanieczyszczać swojej przestrzeni nazw ... po prostu nazwij funkcję, my_gena następnie zrób my_gen = my_gen().
6502
2
możesz również użyć, del _my_genjeśli nie chcesz mylić tych dwóch
John La Rooy

Odpowiedzi:

133
for x in iter(int, 1): pass
  • Dwa argumenty iter= wywoływalne zeroargumentowe + wartość wartownika
  • int() zawsze wraca 0

Dlatego iter(int, 1)jest nieskończoną iteratorem. Oczywiście istnieje ogromna liczba odmian tego konkretnego tematu (szczególnie po dodaniu lambdado miksu). Jednym z wariantów szczególnej uwagi jest to iter(f, object()), że użycie świeżo utworzonego obiektu jako wartości wartowniczej prawie gwarantuje nieskończony iterator, niezależnie od wywołania używanego jako pierwszy argument.

ncoghlan
źródło
3
bardzo ciekawy sposób wykorzystania iterwłaściwości, o intktórych wielokrotnie zapominamy.
Senthil Kumaran
3
możesz użyć tego magicznego przepisu do symulacji itertools.count:count = lambda start=0, step=1: (start + i*step for i, _ in enumerate(iter(int, 1)))
Coffee_Table
1
Wystarczy, aby wyjaśnić, co tu się dzieje: Gdy iter-function jest wywoływana z dwoma argumentami, to zachowuje się trochę inaczej niż zwykle: iter(callable, sentinel) -> iterator. Argument 1 callablejest wywoływany dla każdej iteracji iteratora, dopóki nie zwróci wartości sentinel. Jednak, jak int()zawsze wrócimy 0, możemy dzwonić na int()zawsze i nigdy nie osiągnąć 1. To w efekcie stworzy nieskończoną listę 0's
Olsgaard
217

itertools zapewnia trzy nieskończone generatory:

Nie znam innych w standardowej bibliotece.


Ponieważ poprosiłeś o jedną linijkę:

__import__("itertools").count()
Katriel
źródło
18
Re: repeat (x, times = ∞) - nie ma symbolu dla tego, kto się zastanawiał - pominięcie argumentu powoduje, że powtarzanie trwa na zawsze
Mr_and_Mrs_D
Głosowano za, ponieważ (chociaż odpowiedź ncoghlan bezpośrednio odnosi się do pytania PO), ma to bardziej ogólne zastosowanie.
Huw Walters
To cholernie dużo bardziej czytelne niż iter(int, 1)zaklęcie. Szkoda, itertoolsże nie ma endlessly()metody, której jedynym celem jest zrobienie tego; itertools.count()też nie jest aż tak czytelny.
BallpointBen
19

możesz iterować po wywołanym zwracaniu stałej zawsze innej niż wartownik iter ()

g1=iter(lambda:0, 1)
user237419
źródło
8
Zarówno kocham, jak i nienawidzę tego ... Uwielbiam to, że osiąga to, czego chcę w tak niewielu postaciach, ale nienawidzę tego, że nikt nigdy na to nie spojrzy i nie będzie wiedział, co ma robić.
ArtOfWarfare
1
znając składnię iter(tutaj z dodatkowym wartownikiem) i składnię lambda(tutaj bez żadnych przekazanych parametrów, po prostu return 0), jedyne miejsce do nienawiści jest to enigmatyczne g1.
Sławomir Lenart
@ SławomirLenart mężczyźni nigdy tego nie rozumieją. po prostu był żenująco mały, więc spuściłem 1g.
user237419
8

Twój system operacyjny może udostępniać coś, co może być używane jako nieskończony generator. Np. Na Linuksie

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

oczywiście nie jest to tak wydajne jak

for i in __import__('itertools').repeat(0)
    print i
John La Rooy
źródło
11
Rozwiązanie / dev / urandom opiera się na tym, \nże od czasu do czasu pojawiają się znaki ... Podstępne! :)
hugomg
5

Żaden, który nie używa wewnętrznie innego nieskończonego iteratora zdefiniowanego jako klasa / funkcja / generator (nie -wyrażenie, funkcja z yield). Wyrażenie generatora zawsze pobiera z innego iterowalnego i nie robi nic poza filtrowaniem i mapowaniem jego elementów. Nie możesz przejść od elementów skończonych do nieskończonych, mając tylko mapi filter, czego potrzebujesz while(lub coś for, co się nie kończy, co jest dokładnie tym, czego nie możemy mieć przy użyciu tylko fori skończonych iteratorów).

Ciekawostka: PEP 3142 jest pozornie podobny, ale po bliższym przyjrzeniu się wydaje się, że nadal wymaga forklauzuli (więc nie (0 while True)dla ciebie), tj. Zapewnia tylko skrót do itertools.takewhile.


źródło
Tak jak podejrzewałem ... Czy możemy być więc pewni, że nie ma łatwo dostępnego nieskończonego generatora do nadużyć? (Niestety, xrange (0,1, -1) nie działa ...)
hugomg
2
@missingno: from itertools import repeat, count, cycleprawdopodobnie liczy się jako „łatwo dostępny” dla większości ludzi.
ncoghlan,
1
Ups, zapomniałem o dwóch argumentach iter. Nieskończone iteratory są faktycznie dostępne jako wbudowane - zobacz moją odpowiedź :)
ncoghlan
5

Dość brzydki i szalony (jednak bardzo zabawny), ale możesz zbudować swój własny iterator z wyrażenia, używając kilku sztuczek (bez "zanieczyszczania" przestrzeni nazw zgodnie z wymaganiami):

{ 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
źródło
Wyraźnie kochasz LISP
Faissaloo
1
@Faissaloo Rzeczywiście ... Możesz znaleźć jeszcze bardziej szalone wyrażenie na starej stronie, którą napisałem: baruchel.github.io/python/2018/06/20/…
Thomas Baruchel
2

Może przydałbyś się na przykład dekoratorów takich jak ten:

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

Użycie (1):

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

for i in blah():
    print i

Zastosowanie (2)

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

Myślę, że można by jeszcze bardziej ulepszyć pozbycie się tych brzydkich (). Jednak zależy to od złożoności sekwencji, którą chcesz móc utworzyć. Mówiąc ogólnie, jeśli twoja sekwencja może być wyrażona za pomocą funkcji, wówczas cała złożoność i cukier składniowy generatorów może być ukryta wewnątrz dekoratora lub funkcji podobnej do dekoratora.

julx
źródło
9
OP prosi o oneliner, a ty przedstawiasz 10-liniowy dekorator z potrójnym zagnieżdżeniem defi zamknięciem? ;)
2
@delnan Cóż, ale jeśli raz zdefiniujesz dekoratora, możesz mieć swój jeden wkład, prawda? Jak rozumiem, celem jest zaimplementowanie każdego dodatkowego generatora nieskończonego w jednej linii. I to właśnie jest tutaj przedstawione. Możesz mieć (2^x), możesz mieć (x). Jeśli trochę
poprawisz,
Nie odpowiada na moje pytanie, ale jak możesz nie kochać tych wszystkich puszystych zamknięć? Przy okazji, jestem prawie pewien, że możesz pozbyć się dodatkowych parenów, pozbywając się seqi wciskając kod bezpośrednio dowrap
hugomg