Pythonでzip(* [iter(s)] * n)はどのように機能しますか?


103
s = [1,2,3,4,5,6,7,8,9]
n = 3

zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)]

どのように機能しzip(*[iter(s)]*n)ますか?より冗長なコードで記述された場合、どのように見えますか?


1
それがどのように動作するかを説明もされた場合も、ここで見てみましょう:stackoverflow.com/questions/2202461/...を
マット・ジョイナー

ここでの答えが十分ではない場合、私はそれをここにブログ: telliott99.blogspot.com/2010/01/...
telliott99

7
このテクニックは非常に興味深いものですが、Pythonの中核的な「読みやすさ」の価値に反するものでなければなりません。
Demis

回答:


108

iter()シーケンスのイテレータです。各要素がであるの数量を[x] * n含むリスト、つまり長さのリストを生成します。関数呼び出しのシーケンスを引数にアンパックします。したがって、同じイテレータを3回に渡していて、毎回イテレータからアイテムをプルします。nxnx*argzip()

x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)

1
知っておきたいこと:イテレータがアイテムをyields(= returns)するとき、このアイテムは「消費された」と想像できます。したがって、次にイテレータが呼び出されると、次の「未使用」アイテムが生成されます。
winklerrr

46

その他のすばらしい回答とコメントは、引数の解凍zip()の役割をよく説明しています。

以下のようイグナシオujukatzel言う、あなたはに渡すzip()同じイテレータに3つの参照およびzip()オーダーからイテレータをそれぞれ参照して整数-の3つのタプルになります:

1,2,3,4,5,6,7,8,9  1,2,3,4,5,6,7,8,9  1,2,3,4,5,6,7,8,9
^                    ^                    ^            
      ^                    ^                    ^
            ^                    ^                    ^

さらに詳細なコードサンプルを要求するので、

chunk_size = 3
L = [1,2,3,4,5,6,7,8,9]

# iterate over L in steps of 3
for start in range(0,len(L),chunk_size): # xrange() in 2.x; range() in 3.x
    end = start + chunk_size
    print L[start:end] # three-item chunks

startおよびの値に従うend

[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]

FWIW、次map()の初期引数で同じ結果を得ることができますNone

>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]

詳細についてzip()map()http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/


31

(おそらくイテレータに詳しい人には明白ですが)すべての回答で見落とされているものの、他の人にはそれほど明白ではないものがあると思います-

同じイテレーターがあるため、消費され、残りの要素はzipによって使用されます。したがって、単にイテラーではなくリストを使用した場合などです。

l = range(9)
zip(*([l]*3)) # note: not an iter here, the lists are not emptied as we iterate 
# output 
[(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8)]

イテレータを使用して、値をポップし、利用可能なままにしておきます。そのため、zipの場合、0が消費されると1が利用可能になり、2が続きます。非常に微妙なことですが、かなり賢いです!!!


+1、あなたは私を救った!誰もがこれを知っていると仮定すると、他の回答がこの重要な詳細をスキップしたとは信じられません。この情報を含むドキュメントへの参照を提供できますか?
Snehasish Karmakar

9

iter(s) sの反復子を返します。

[iter(s)]*n sの同じ反復子のn倍のリストを作成します。

そのため、を実行するzip(*[iter(s)]*n)と、リストから3つすべてのイテレータからアイテムが順番に抽出されます。すべてのイテレータは同じオブジェクトなので、リストをのチャンクにグループ化するだけですn


7
「同じリストのn個の反復子」ではなく、「n回同じ反復子オブジェクト」。異なるイテレータオブジェクトは、同じリストであっても状態を共有しません。
Thomas Wouters、

ありがとう、修正しました。確かにそれは私が「考えている」ことでしたが、何か他のものを書きました。
ツイスター

6

この方法でzipを使用するための1つのアドバイス。リストの長さが均等に割り切れない場合は、リストが切り捨てられます。これを回避するには、フィル値を受け入れることができる場合はitertools.izip_longestを使用できます。または、次のようなものを使用できます。

def n_split(iterable, n):
    num_extra = len(iterable) % n
    zipped = zip(*[iter(iterable)] * n)
    return zipped if not num_extra else zipped + [iterable[-num_extra:], ]

使用法:

for ints in n_split(range(1,12), 3):
    print ', '.join([str(i) for i in ints])

プリント:

1, 2, 3
4, 5, 6
7, 8, 9
10, 11

3
これはすでにitertoolsレシピに記載されています: docs.python.org/2/library/itertools.html#recipes grouper。ホイールを再発明する必要はありません
jamylak 2013

1

Pythonインタプリタにまたは何が起こっているかを確認するために、おそらく簡単ですipythonn = 2

In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]

したがって、同じイテレータオブジェクトを指している2つのイテレータのリストがあります。ことを覚えておいてくださいiterオブジェクトのイテレータオブジェクトを返すと、このシナリオでは、それは二回に起因する同じイテレータである*2パイソン糖衣構文。イテレータも1回だけ実行されます。

さらに、zip任意の数の反復可能オブジェクト(シーケンス反復可能)を取り、各入力シーケンスのi番目の要素からタプルを作成します。今回のケースでは両方のイテレーターが同一であるため、zipは、出力の2要素のタプルごとに同じイテレーターを2回移動します。

In [41]: help(zip)
Help on built-in function zip in module __builtin__:

zip(...)
    zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]

    Return a list of tuples, where each tuple contains the i-th element
    from each of the argument sequences.  The returned list is truncated
    in length to the length of the shortest argument sequence.

アンパック(*)演算子 2要素のタプルを作成するのに十分な入力が存在しなくなるまで、この場合にあるイテレータが枯渇に実行することを保証します。

これは、任意の値に拡張できます nzip(*[iter(s)]*n)説明したように動作します。


遅くなってすみません。しかし、「* 2 python構文上の砂糖のために同じイテレータを2回使用します。イテレータも1回しか実行しません」と説明してください。一部お願いします?もしそうなら、結果はどうして[( "A"、 "A")....]にならないのですか?ありがとう。
Bowen Liu

@BowenLiu *は、オブジェクトを複製するのに便利です。スカラーで、次にリストで試してください。print(*zip(*[iter("ABCDEFG")]*2))vs も試してくださいprint(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")]))。次に、2つを小さなステップに分解して、2つのステートメントの実際のイテレータオブジェクトが何であるかを確認します。
アカン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.