Trình lặp danh sách tròn trong Python


99

Tôi cần lặp lại danh sách vòng tròn, có thể nhiều lần, mỗi lần bắt đầu với mục được truy cập cuối cùng.

Trường hợp sử dụng là một nhóm kết nối. Một ứng dụng khách yêu cầu kết nối, một trình lặp kiểm tra xem kết nối trỏ đến có sẵn không và trả về kết nối đó, nếu không sẽ lặp lại cho đến khi tìm thấy kết nối khả dụng.

Có cách nào gọn gàng để làm điều đó bằng Python không?

Câu trả lời:


159

Sử dụng itertools.cycle, đó là mục đích chính xác của nó:

from itertools import cycle

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

pool = cycle(lst)

for item in pool:
    print item,

Đầu ra:

a b c a b c ...

(Vòng lặp mãi mãi, hiển nhiên)


Để nâng cấp trình vòng lặp theo cách thủ công và lấy từng giá trị từ nó, chỉ cần gọi next(pool):

>>> next(pool)
'a'
>>> next(pool)
'b'

1
Bạn đang in các mục trong một vòng lặp. Điều gì tôi muốn rời khỏi vòng lặp và quay lại sau? (Tôi muốn bắt đầu từ nơi tôi đã dừng lại).
user443854

7
@ user443854 sử dụng pool.next()để lấy một mặt hàng tiếp theo duy nhất từ ​​chu kỳ
Jacob Krall

4
@ user443854 FWIW đây là một câu trả lời tốt hơn nhiều so với của tôi. Không có lý do gì để thực hiện lại các chức năng thư viện!
Jacob Krall

5
pool.next () không hoạt động với tôi, chỉ tiếp theo (pool). Có lẽ là do Python 3?
fjsj

6
@fjsj đó là chính xác, trên Python 3, bạn cần sử dụng next(iterator)(BTW cũng hoạt động tốt trên Python 2.x, và do đó là dạng chuẩn nên được sử dụng). Xem Trình phát điện.next () có hiển thị trong python 3.0 không? để được giải thích sâu hơn. Đã cập nhật câu trả lời của tôi cho phù hợp.
Lukas Graf

54

Câu trả lời đúng là sử dụng itertools.cycle . Nhưng, giả sử rằng hàm thư viện không tồn tại. Bạn sẽ thực hiện nó như thế nào?

Sử dụng máy phát điện :

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

Sau đó, bạn có thể sử dụng một forcâu lệnh để lặp vô hạn hoặc bạn có thể gọi next()để nhận một giá trị tiếp theo duy nhất từ ​​trình lặp của trình tạo:

connections = circular()
next(connections) # 'a'
next(connections) # 'b'
next(connections) # 'c'
next(connections) # 'a'
next(connections) # 'b'
next(connections) # 'c'
next(connections) # 'a'
#....

Đẹp! Làm thế nào nó biết để bắt đầu lại khi danh sách đã hết?
user443854

1
@ user443854 while Truephương tiện để lặp lại mãi mãi
Jacob Krall

2
@juanchopanza: Đúng; itertools.cyclelà một câu trả lời tốt hơn. Điều này cho thấy cách bạn có thể viết cùng một chức năng nếu itertoolskhông có sẵn :)
Jacob Krall

Trình tạo đơn giản cũng lưu một bản sao của mỗi phần tử như thế itertools.cyclenào? Hay máy phát điện đơn giản sẽ là một thiết kế tiết kiệm bộ nhớ hơn? Theo các cycletài liệu :Note, this member of the toolkit may require significant auxiliary storage (depending on the length of the iterable).
dthor

2
@dthor trình tạo này tạo một danh sách với ba phần tử và viết chữ trên đó, sau đó hủy danh sách và tạo một danh sách mới, vĩnh viễn. Tài liệu đó cyclengụ ý rằng đầu vào có thể lặp lại được chuyển đổi thành listtrước khi trình tạo của nó khởi động, vì iterablechỉ "tốt cho một lần chuyển qua tập giá trị".
Jacob Krall

9

Hoặc bạn có thể làm như sau:

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

in abcdefab c ... mãi mãi


3

bạn có thể thực hiện điều này với append(pop())vòng lặp:

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

hoặc for i in range()vòng lặp:

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

hoặc đơn giản:

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

while 1:
    for i in l:
       print i

tất cả đều in:

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

trong ba cách tiếp cận, tôi sẽ có xu hướng tiếp cận append (pop ()) như một hàm

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]

Ủng hộ điều này vì nó đã giúp tôi với một trường hợp sử dụng hoàn toàn khác, nơi tôi chỉ muốn lặp lại danh sách một số lần, mỗi lần với phần tử bắt đầu tiến một bước. Trường hợp sử dụng của tôi là lặp lại các người chơi trong một trò chơi poker, tiến lên của người chia bài sẽ đẩy một người chơi về phía trước cho mỗi vòng.
Johan

2

Bạn cần một trình lặp tùy chỉnh - Tôi sẽ điều chỉnh trình lặp từ câu trả lời này .

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

2

Nếu bạn muốn quay vòng nthời gian, hãy triển khai ncycles công thức 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']
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.