Pythonでリストをペア(現在、次)として反復する


131

「現在の」要素と「次の」要素を見て、Pythonでリストを反復処理する必要がある場合があります。私は今まで、次のようなコードでそうしました:

for current, next in zip(the_list, the_list[1:]):
    # Do something

これは機能し、私が期待することを行いますが、同じことを行うためのより慣用的または効率的な方法はありますか?


この質問については MizardXの回答を確認してください。しかし、私はこのソリューションがあなたのものより慣用的であるとは思いません。
ファビオ・Diniz


39
他の誰もそれを言及していないので、私はその人になります、そしてnextこの方法を使用すると組み込みがマスクされることを指摘します。
センダーレ2011年

@senderle多分それはPython 2です…
Quintec

2
@ thecoder16:nextPython 2の組み込み関数でもあります
zondo

回答:


131

以下はitertoolsモジュールのドキュメントからの関連する例です:

import itertools
def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = itertools.tee(iterable)
    next(b, None)
    return zip(a, b)   

Python 2の場合は、次のitertools.izip代わりに必要ですzip

import itertools
def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = itertools.tee(iterable)
    next(b, None)
    return itertools.izip(a, b)

仕組み:

まず、二つの平行なイテレータ、aおよびb作成される(tee()コール)、オリジナルイテラブルの最初の要素の両方指します。2番目のイテレーターbは1ステップ先に移動します(next(b, None))呼び出し)。この時点でa、s0をb指し、s1 を指します。双方ab独立オリジナルイテレータを横切ることができる- izip関数は、2回の反復子をとり、同じペースで、両方のイテレータを前進、返された要素のペアを作ります。

注意点:このtee()関数は、互いに独立して進行できる2つの反復子を生成しますが、コストがかかります。イテレータの1つが他のイテレータより先に進む場合tee() 、2番目のイテレータもそれらを消費するまで、消費された要素をメモリに保持する必要があります(元のイテレータを「巻き戻す」ことはできません)。ここでは、1つのイテレータが他のイテレータよりも1ステップだけ進んでいるため問題ではありませんが、一般に、この方法で多くのメモリを使用するのは簡単です。

パラメータtee()をとることができるのでn、これは3つ以上の並列イテレータにも使用できます。

def threes(iterator):
    "s -> (s0,s1,s2), (s1,s2,s3), (s2, s3,4), ..."
    a, b, c = itertools.tee(iterator, 3)
    next(b, None)
    next(c, None)
    next(c, None)
    return zip(a, b, c)

4
サンプルコードは素晴らしいです...しかし、なぜそれが機能するのかについて少し説明してもらえますか?「tee()」と「next()」がここで何をしているのかを言うように。
John Mulder、

@John Mulder:短い要約をしました。
ラファウDowgird

9
zip(ł, ł[1:])はるかに短いと神託で
noɥʇʎԀʎzɐɹƆ

2
@noɥʇʎԀʎzɐɹƆ:いいえ、すべてのイテラブルで機能するわけではなく、リストで使用すると不要なコピーが作成されます。関数の使用はpythonicです。
Ry-

実装され、この機能funcyモジュール:funcy.pairwisefuncy.readthedocs.io/en/stable/seqs.html#pairwise
ADR

30

自分でロール!

def pairwise(iterable):
    it = iter(iterable)
    a = next(it, None)

    for b in it:
        yield (a, b)
        a = b

1
ちょうど私が必要なもの!これはpythonメソッドとして不滅化されましたか、それともローリングを続ける必要がありますか?
uhoh

1
@uhoh:私の知る限りではまだ!
Ry-

21

以来the_list[1:]、実際に(その最初の要素を除く)全リストのコピーを作成し、zip()あなたのリストの合計3枚のコピーには、呼び出されたときにすぐにタプルのリストを作成しますが作成されます。リストが非常に大きい場合は、

from itertools import izip, islice
for current_item, next_item in izip(the_list, islice(the_list, 1, None)):
    print(current_item, next_item)

リストはまったくコピーされません。


3
Python 3.xでは、izipがitertoolsで抑制されているため、組み込みのzipを使用する必要があります
Xavier Combelle

1
実際、the_list[1:]リスト全体のコピーではなく、スライスオブジェクトを作成するだけではありません。つまり、OPのテクニックは、サウンドを作成するほど無駄ではありません。
martineau

3
私は、リストに渡される[1:]スライスオブジェクト(または " 1:")を__slice__作成し、選択した要素のみを含むコピーを返すと思います。リストをコピーする慣用的な方法の1つはl_copy = l[:](私は醜く、判読できないと思います-優先l_copy = list(l)
dcrosta

4
@dcrosta:__slice__特別な方法はありません。 the_list[1:]はと同等the_list[slice(1, None)]で、はと同等list.__getitem__(the_list, slice(1, None))です。
Sven Marnach

4
@martineau:によって作成されたコピーthe_list[1:]は浅いコピーであるため、リストアイテムごとに1つのポインターのみで構成されます。より多くのメモリを消費する部分はzip()それ自体です。tupleリスト項目ごとに1つのインスタンスのリストを作成し、各インスタンスには2つの項目への2つのポインタといくつかの追加情報が含まれるためです。このリストは、コピーによって[1:]消費されるメモリ量の9倍を消費します。
Sven Marnach

19

私はこれを公開しているだけです。誰もenumerate()について考えたことがないことに非常に驚いています。

for (index, thing) in enumerate(the_list):
    if index < len(the_list):
        current, next_ = thing, the_list[index + 1]
        #do something

11
実際には、ifあなたがスライシングを使用している場合も削除することができます:for (index, thing) in enumerate(the_list[:-1]): current, next_ = thing, the_list[index + 1]
lifebalance

2
これは本当に答えになるはずです。追加のインポートに依存せず、うまく機能します。
jamescampbell 2018

ただし、インデックス付けできないイテラブルでは機能しないため、一般的なソリューションではありません。
WIM

14

インデックスによる反復は同じことを行うことができます:

#!/usr/bin/python
the_list = [1, 2, 3, 4]
for i in xrange(len(the_list) - 1):
    current_item, next_item = the_list[i], the_list[i + 1]
    print(current_item, next_item)

出力:

(1, 2)
(2, 3)
(3, 4)

あなたの答えは、質問のように、現在ではなく、以前現在でした。常に現在の要素のインデックスになるように、セマンティクスを改善する編集を行いました。i
Bengt 2012

1

これは2020年5月16日現在の単純なインポートです

from more_itertools import pairwise
for current, next in pairwise(your_iterable):
  print(f'Current = {current}, next = {nxt}')

more-itertoolsのドキュメント このコードは内部的には他の回答と同じですが、可能な場合はインポートを使用することを強くお勧めします。

まだインストールしていない場合: pip install more-itertools

たとえば、フィボナッチ数列がある場合、後続のペアの比率を次のように計算できます。

from more_itertools import pairwise
fib= [1,1,2,3,5,8,13]
for current, nxt in pairwise(fib):
    ratio=current/nxt
    print(f'Curent = {current}, next = {nxt}, ratio = {ratio} ')

0

リスト内包表記を使用したリストからのペア

the_list = [1, 2, 3, 4]
pairs = [[the_list[i], the_list[i + 1]] for i in range(len(the_list) - 1)]
for [current_item, next_item] in pairs:
    print(current_item, next_item)

出力:

(1, 2)
(2, 3)
(3, 4)

0

誰も短くてシンプルで最も重要な一般的な解決策について言及していないことに本当に驚いています:

Python 3:

from itertools import islice

def n_wise(iterable, n):
    return zip(*(islice(iterable, i, None) for i in range(n)))

Python 2:

from itertools import izip, islice

def n_wise(iterable, n):
    return izip(*(islice(iterable, i, None) for i in xrange(n)))

を渡すことでペアワイズ反復で機能しますがn=2、それ以上の数を処理できます。

>>> for a, b in n_wise('Hello!', 2):
>>>     print(a, b)
H e
e l
l l
l o
o !

>>> for a, b, c, d in n_wise('Hello World!', 4):
>>>     print(a, b, c, d)
H e l l
e l l o
l l o
l o   W
o   W o
  W o r
W o r l
o r l d
r l d !

-2

基本的なソリューション:

def neighbors( list ):
  i = 0
  while i + 1 < len( list ):
    yield ( list[ i ], list[ i + 1 ] )
    i += 1

for ( x, y ) in neighbors( list ):
  print( x, y )

-2
code = '0016364ee0942aa7cc04a8189ef3'
# Getting the current and next item
print  [code[idx]+code[idx+1] for idx in range(len(code)-1)]
# Getting the pair
print  [code[idx*2]+code[idx*2+1] for idx in range(len(code)/2)]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.