Apakah ada ekspresi untuk generator tak terbatas?

119

Apakah ada ekspresi generator lurus ke depan yang dapat menghasilkan elemen tak hingga?

Ini adalah pertanyaan teoritis murni. Tidak perlu jawaban "praktis" di sini :)


Misalnya, mudah membuat generator terbatas:

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

Namun, untuk membuat yang tak terbatas, saya perlu "mencemari" namespace saya dengan fungsi palsu:

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

Melakukan sesuatu dalam file terpisah dan import-ing nanti tidak dihitung.


Saya juga tahu bahwa itertools.repeatitulah tepatnya. Saya ingin tahu apakah ada solusi satu baris tanpa itu.

hugomg
sumber
9
Anda sebenarnya tidak perlu mencemari namespace Anda ... cukup beri nama fungsinya my_genlalu lakukan my_gen = my_gen().
6502
2
Anda juga dapat menggunakan del _my_genjika Anda tidak ingin membingungkan keduanya
John La Rooy

Jawaban:

133
for x in iter(int, 1): pass
  • Dua-argumen iter= nilai callable + sentinel nol-argumen
  • int() selalu kembali 0

Oleh karena itu, iter(int, 1)merupakan iterator tak terbatas. Jelas ada sejumlah besar variasi pada tema khusus ini (terutama setelah Anda menambahkannya lambdake dalam campuran). Salah satu varian dari catatan tertentu adalah iter(f, object()), karena menggunakan objek yang baru dibuat sebagai nilai sentinel hampir menjamin iterator tak terbatas terlepas dari callable yang digunakan sebagai argumen pertama.

ncoghlan.dll
sumber
3
cara yang sangat menarik untuk digunakan iterdengan properti intyang sering kali kita lupakan.
Senthil Kumaran
3
Anda dapat menggunakan resep ajaib ini untuk mensimulasikan itertools.count:count = lambda start=0, step=1: (start + i*step for i, _ in enumerate(iter(int, 1)))
Coffee_Table
1
Hanya untuk menjelaskan apa yang terjadi di sini: Bila iter-fungsi disebut dengan dua argumen, itu berperilaku sedikit berbeda dari biasanya: iter(callable, sentinel) -> iterator. Argumen 1, callabledipanggil untuk setiap iterasi dari iterator, hingga menghasilkan nilai sentinel. Namun, seperti yang int()akan selalu kembali 0, kita dapat menelepon int()selamanya dan tidak pernah mencapai 1. Ini akan menghasilkan daftar tak terbatas dari 0's
Olsgaard
217

itertools menyediakan tiga generator tak terbatas:

Saya tidak tahu ada orang lain di perpustakaan standar.


Karena Anda meminta satu baris:

__import__("itertools").count()
Katriel
sumber
18
Re: ulangi (x, kali = ∞) - tidak ada simbol untuk siapa pun yang bertanya-tanya - menghilangkan argumen membuat pengulangan berjalan selamanya
Mr_and_Mrs_D
Suara positif karena (sementara jawaban ncoghlan secara langsung menjawab pertanyaan OP) ini lebih berlaku secara umum.
Huw Walters
Ini jauh lebih bisa dibaca daripada iter(int, 1)mantera. Sayang sekali itertoolstidak memiliki endlessly()metode yang tujuan utamanya adalah melakukan ini; itertools.count()juga tidak begitu mudah dibaca.
BallpointBen
19

Anda dapat mengulangi callable yang mengembalikan konstanta yang selalu berbeda dari sentinel iter ()

g1=iter(lambda:0, 1)
pengguna237419
sumber
8
Saya suka dan benci ini ... Saya suka itu mencapai apa yang saya inginkan dalam begitu sedikit karakter, tapi benci bagaimana tidak ada yang akan melihatnya dan tahu apa yang seharusnya dilakukan.
ArtOfWarfare
1
mengetahui sintaks iter(di sini dengan tambahan sentinel) dan sintaks lambda(di sini tanpa parameter yang lewat, hanya return 0), satu-satunya tempat untuk dibenci adalah yang membingungkan g1.
Sławomir Lenart
Pria @ SławomirLenart tidak pernah mengerti. hanya saja itu sangat kecil jadi saya menyemprotkan 1g.
pengguna237419
8

OS Anda mungkin menyediakan sesuatu yang dapat digunakan sebagai generator tak terbatas. Misal di linux

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

jelas ini tidak seefisien

for i in __import__('itertools').repeat(0)
    print i
John La Rooy
sumber
11
Solusi / dev / urandom bergantung pada \ns yang muncul dari waktu ke waktu ... Licik! :)
hugomg
5

Tidak ada yang tidak secara internal menggunakan iterator tak terbatas lainnya yang didefinisikan sebagai kelas / fungsi / generator (bukan -ekspresi, fungsi dengan yield). Ekspresi generator selalu diambil dari iterable anoter dan tidak melakukan apa pun selain memfilter dan memetakan itemnya. Anda tidak dapat beralih dari item terbatas ke item tak terbatas hanya dengan mapdan filter, Anda perlu while(atau foryang tidak berakhir, itulah yang sebenarnya tidak dapat kita gunakan hanyafor dan iterator terbatas).

Trivia: PEP 3142 secara dangkal mirip, tetapi setelah diperiksa lebih dekat tampaknya masih memerlukan forklausa (jadi tidak (0 while True)untuk Anda), yaitu hanya menyediakan pintasan untuk itertools.takewhile.


sumber
Seperti yang saya duga ... Bisakah kita yakin bahwa tidak ada generator tak terbatas yang tersedia untuk disalahgunakan? (Sayangnya, xrange (0,1, -1) tidak berfungsi ...)
hugomg
2
@missingno: from itertools import repeat, count, cyclemungkin dianggap "tersedia" bagi kebanyakan orang.
ncoghlan
1
Ups, saya lupa tentang 2 argumen iter. Iterator tak terbatas sebenarnya tersedia sebagai bawaan - lihat jawaban saya :)
ncoghlan
5

Cukup jelek dan gila (namun sangat lucu), tetapi Anda dapat membuat iterator Anda sendiri dari ekspresi dengan menggunakan beberapa trik (tanpa "mengotori" namespace Anda seperti yang diperlukan):

{ 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
sumber
Anda jelas menyukai LISP
Faissaloo
1
@Faissaloo Memang ... Anda mungkin menemukan ekspresi yang lebih gila di halaman lama saya menulis: baruchel.github.io/python/2018/06/20/…
Thomas Baruchel
2

Mungkin Anda bisa menggunakan dekorator seperti ini misalnya:

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

Penggunaan (1):

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

for i in blah():
    print i

Penggunaan (2)

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

Saya pikir itu bisa lebih ditingkatkan untuk menyingkirkan mereka yang jelek (). Namun itu tergantung pada kompleksitas urutan yang ingin Anda buat. Secara umum jika urutan Anda dapat diekspresikan menggunakan fungsi, maka semua kerumitan dan sintaksis generator dapat disembunyikan di dalam dekorator atau fungsi seperti dekorator.

julx
sumber
9
OP meminta oneliner dan Anda menyajikan dekorator 10-baris dengan triple-nested defdan closure? ;)
2
@delnan Nah, tetapi jika Anda mendefinisikan dekorator sekali, Anda dapat memiliki satu liner, bukan? Seperti yang saya pahami, tujuannya adalah agar setiap generator tak terbatas tambahan diimplementasikan dalam satu baris. Dan inilah yang disajikan di sini. Anda dapat memiliki (2^x), Anda dapat memiliki (x). Jika Anda meningkatkannya sedikit mungkin juga fibonacci, dll.
julx
Tidak menjawab pertanyaanku, tapi bagaimana mungkin kau tidak menyukai semua penutup lembut itu? BTW, saya cukup yakin Anda dapat menghilangkan tanda kurung ekstra dengan membuang seqdan memasukkan kembali kode secara langsung kewrap
hugomg