Iterator daftar melingkar dengan Python

99

Saya perlu mengulang daftar melingkar, mungkin berkali-kali, setiap kali dimulai dengan item yang terakhir dikunjungi.

Kasus penggunaan adalah kumpulan koneksi. Klien meminta koneksi, iterator memeriksa apakah koneksi menunjuk-ke ​​tersedia dan mengembalikannya, jika tidak mengulang sampai menemukan koneksi yang tersedia.

Apakah ada cara yang rapi untuk melakukannya dengan Python?

pengguna443854
sumber

Jawaban:

159

Gunakan itertools.cycle, itulah tujuan tepatnya:

from itertools import cycle

lst = ['a', 'b', 'c']

pool = cycle(lst)

for item in pool:
    print item,

Keluaran:

a b c a b c ...

(Loop selamanya, jelas)


Untuk memajukan iterator secara manual dan menarik nilai darinya satu per satu, cukup panggil next(pool):

>>> next(pool)
'a'
>>> next(pool)
'b'
Lukas Graf
sumber
1
Anda mencetak item dalam satu lingkaran. Apa yang ingin saya tinggalkan dan kembali lagi nanti? (Saya ingin memulai dari bagian yang saya tinggalkan sebelumnya).
pengguna443854
7
@ pengguna443854 gunakan pool.next()untuk mendapatkan satu item berikutnya dari siklus
Jacob Krall
4
@ user443854 FWIW ini adalah jawaban yang jauh lebih baik dari saya. Tidak ada alasan untuk berkeliling menerapkan kembali fungsi perpustakaan!
Jacob Krall
5
pool.next () tidak berhasil untuk saya, hanya next (pool). Mungkin karena Python 3?
fjsj
6
@fjsj benar, pada Python 3 Anda perlu menggunakan next(iterator)(yang BTW juga berfungsi dengan baik pada Python 2.x, dan karena itu adalah bentuk kanonik yang harus digunakan). Lihat Apakah generator.next () terlihat di python 3.0? untuk penjelasan yang lebih mendalam. Memperbarui jawaban saya.
Lukas Graf
54

Jawaban yang benar adalah dengan menggunakan itertools.cycle . Tapi, mari kita asumsikan bahwa fungsi perpustakaan tidak ada. Bagaimana Anda akan menerapkannya?

Gunakan generator :

def circular():
    while True:
        for connection in ['a', 'b', 'c']:
            yield connection

Kemudian, Anda dapat menggunakan forpernyataan untuk mengulang tanpa batas, atau Anda dapat memanggil next()untuk mendapatkan satu nilai berikutnya dari iterator generator:

connections = circular()
next(connections) # 'a'
next(connections) # 'b'
next(connections) # 'c'
next(connections) # 'a'
next(connections) # 'b'
next(connections) # 'c'
next(connections) # 'a'
#....
Jacob Krall
sumber
Bagus! Bagaimana cara mengetahui untuk memulai kembali ketika daftar habis?
pengguna443854
1
@ user443854 while Truecara untuk mengulang selamanya
Jacob Krall
2
@juanchopanza: Ya; itertools.cycleadalah jawaban yang lebih baik. Ini menunjukkan bagaimana Anda dapat menulis fungsi yang sama jika itertoolstidak tersedia :)
Jacob Krall
Apakah generator sederhana juga menyimpan salinan setiap elemen seperti yang itertools.cycledilakukan? Atau generator sederhana akan menjadi desain yang lebih hemat memori? Per cycledocs :Note, this member of the toolkit may require significant auxiliary storage (depending on the length of the iterable).
dthor
2
@dthor generator ini membuat daftar dengan tiga elemen dan melek di atasnya, lalu memusnahkan daftar dan membuat yang baru, untuk selamanya. Dokumentasi untuk itu cyclemenyiratkan bahwa input yang dapat diulang diubah menjadi listsebelum generatornya dimulai, karena iterablehanya "baik untuk satu lintasan di atas kumpulan nilai".
Jacob Krall
9

Atau Anda bisa melakukan seperti ini:

conn = ['a', 'b', 'c', 'd', 'e', 'f']
conn_len = len(conn)
index = 0
while True:
    print(conn[index])
    index = (index + 1) % conn_len

mencetak abcdefab c ... selamanya

viky.pat
sumber
3

Anda dapat melakukannya dengan append(pop())loop:

l = ['a','b','c','d']
while 1:
    print l[0]
    l.append(l.pop(0))

atau for i in range()loop:

l = ['a','b','c','d']
ll = len(l)
while 1:
    for i in range(ll):
       print l[i]

atau sederhana:

l = ['a','b','c','d']

while 1:
    for i in l:
       print i

semuanya mencetak:

>>>
a
b
c
d
a
b
c
d
...etc.

dari ketiganya, saya akan cenderung menggunakan pendekatan append (pop ()) sebagai fungsi

servers = ['a','b','c','d']

def rotate_servers(servers):
    servers.append(servers.pop(0))
    return servers

while 1:
    servers = rotate_servers(servers)
    print servers[0]
litepresence
sumber
Meningkatkan ini karena membantu saya dengan kasus penggunaan yang sama sekali berbeda di mana saya hanya ingin mengulang daftar beberapa kali, setiap kali dengan elemen start memajukan satu langkah. Kasus penggunaan saya adalah untuk mengulangi para pemain dalam permainan poker, memajukan dealer memajukan satu pemain ke depan untuk setiap putaran.
Johan
2

Anda memerlukan iterator khusus - Saya akan menyesuaikan iterator dari jawaban ini .

from itertools import cycle

class ConnectionPool():
    def __init__(self, ...):
        # whatever is appropriate here to initilize
        # your data
        self.pool = cycle([blah, blah, etc])
    def __iter__(self):
        return self
    def __next__(self):
        for connection in self.pool:
            if connection.is_available:  # or however you spell it
                return connection
Ethan Furman
sumber
2

Jika Anda ingin nmengubah waktu, terapkan ncycles resep itertools :

from itertools import chain, repeat


def ncycles(iterable, n):
    "Returns the sequence elements n times"
    return chain.from_iterable(repeat(tuple(iterable), n))


list(ncycles(["a", "b", "c"], 3))
# ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
pylang
sumber