Pythonの循環リストイテレータ


99

最後に訪問したアイテムから開始するたびに、おそらく何度も循環リストを反復処理する必要があります。

ユースケースは接続プールです。クライアントは接続を要求し、イテレータはポイントされた接続が使用可能かどうかを確認してそれを返します。それ以外の場合は、使用可能な接続が見つかるまでループします。

Pythonでそれを行うためのきちんとした方法はありますか?

回答:


159

を使用してくださいitertools.cycle、それはまさにその目的です:

from itertools import cycle

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

pool = cycle(lst)

for item in pool:
    print item,

出力:

a b c a b c ...

(永遠にループします、明らかに)


イテレータを手動で進め、そこから値を1つずつプルするには、次のように呼び出しますnext(pool)

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

1
アイテムをループで印刷しています。ループを終了して後で戻ってきたいものは何ですか?(中断したところから始めたい)。
user443854 2014年

7
@ user443854を使用pool.next()して、サイクルから次の単一のアイテムを取得
Jacob Krall

4
@ user443854 FWIWこれは私のものよりはるかに良い答えです。ライブラリ関数の再実装を回避する理由はありません!
Jacob Krall、2014年

5
pool.next()は機能しませんでした。おそらくPython 3のためでしょうか?
fjsj 2015

6
@fjsjは正しいです。Python3では使用する必要がありますnext(iterator)(これはBTWもPython 2.xで正常に動作するため、使用するべき正規の形式です)。python 3.0でgenerator.next()が表示されるかを参照してくださいより詳細な説明については。それに応じて私の答えを更新しました。
Lukas Graf、

54

正解はitertools.cycleを使用することです。しかし、ライブラリ関数が存在しないと仮定しましょう。どのように実装しますか?

ジェネレーターを使用する:

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

次に、forステートメントを使用して無限に反復するか、呼び出しnext()てジェネレータイテレータから次の単一の値を取得できます。

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

いいね!リストが使い果たされたときに最初からやり直すことはどのようにしてわかりますか?
user443854 2014年

1
@ user443854 while True永遠に繰り返す手段
ジェイコブクラル

2
@juanchopanza:はい。itertools.cycleより良い答えです。これは、利用できitertoolsない場合に同じ機能を作成する方法を示しています:)
Jacob Krall

単純なジェネレーターも、同様に各要素のコピーを保存itertools.cycleしますか?または、単純なジェネレーターはよりメモリ効率の高い設計でしょうか?cycleドキュメントごと:Note, this member of the toolkit may require significant auxiliary storage (depending on the length of the iterable).
dthor

2
@dthorこのジェネレータは、3つの要素を持つリストを作成し、それを読み書きします。次に、リストを破棄し、永続的に新しいリストを作成します。のドキュメントは、「値のセットに対する1回のパスにのみ有効」であるためcycle、入力イテラブルがlistジェネレーターの起動前に変換されることを示唆していますiterable
Jacob Krall、2016年

9

または、次のようにすることもできます。

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

abcdefab cを印刷...永遠に


3

あなたはappend(pop())ループでこれを達成することができます:

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

またはfor i in range()ループ:

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

または単に:

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

while 1:
    for i in l:
       print i

すべてのプリント:

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

3つのうち私は関数としてappend(pop())アプローチになりがちです

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]

これを支持するのは、開始要素が1ステップ進むたびにリストを何度も繰り返し処理したいというまったく異なる使用例で私を助けたからです。私のユースケースは、ポーカーのゲームでプレイヤーを繰り返し、ディーラーを前進させて各ラウンドで1人のプレイヤーを前に進めることです。
ヨハン

2

カスタムのイテレータが必要です- この答えからイテレータを適応させます。

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タイムを希望する場合は、ncycles 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']
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.