[Python]組み込み関数zipの挙動と使い方

  • 複数のリストをまとめた新たなリストを作る
  • 複数のリストから1つの辞書(dict)を作る

こんなときに活躍するのが組み込み関数zipです。zip関数は引数に取ったイテラブルオブジェクトから要素を集め、タプルのイテレータを作ります。

今回はzip関数の挙動と使い方についてお話します。

zip関数の挙動

関数というからには正しい引数を渡しさえすれば、簡単に使用することができるはずです。まずは使用してみて何が出力されるのかを確かめてみます。

x = [1,2,3,4,5]
zipped_x = zip(x)
print(zipped_x)     #<zip object at 0x7f5da477d480>

関数の戻り値はzipオブジェクトです。そしてこのzipオブジェクトは、__iter__メソッドを持つイテラブルオブジェクトです。

[Python]イテレータとは何者なのか?
今回は初心者が掴みにくい用語上位に来るであろう「イテレータ」と、それに付随する「...

試しにfor文にかけてみましょう。上のコードに追記します。

lst = []
for i in zipped_x:
    lst.append(i)
print(lst)     #[(1,), (2,), (3,), (4,), (5,)]

「1つの値が入ったタプル」を要素に持つリストが出来上がります。つまりzipオブジェクトの中身は、引数に取ったイテラブルオブジェクトの要素をタプルで包み込んだ形のようです。

・zip関数の戻り値はzipオブジェクトというイテラブルオブジェクトである
・zip関数はイテラブルオブジェクトを引数に取り、その要素をタプルで包み込む

ここまでを踏まえて、本題に入ります。

zip関数の使い方

2つのイテラブルを1つに合わせる

ここからはzip関数の使い方を解説していきます。始めに「複数のイテラブルオブジェクトをまとめてループにかける」方法です。

year = [607,710,794,1336,1467]
event = ["遣隋使","平城京","平安京","室町幕府","応仁の乱"]

history = []
for i in zip(year,event):
    history.append(i)
print(history)
#[(607, '遣隋使'), (710, '平城京'), (794, '平安京'), (1336, '室町幕府'), (1467, '応仁の乱')]

zip関数の引数として2つのリストを使い、同時にfor文にかけています。for文に内部変数は「i」1つしかありませんが、2つのリストを組み合わせた新たなリストが作成されています。

yearとevent2つのリストからは互いに1つずつ要素を出し合い、同じインデックス番号の要素同士はタプルとしてまとめられ、for文の中ではこのタプルが1つの値として扱われています。

なお引数に取るイテラブルは3つでも4つでもかまいません。

複数のイテラブルを複数のまま処理する

zipされた値の受け皿が複数あるとどうでしょうか。今度はfor文の内部変数を2つ用意します。

for i,j in zip(year,event):
    print(j+"は"+str(i)+"年")
#遣隋使は607年
#平城京は710年
#平安京は794年
#室町幕府は1336年
#応仁の乱は1467年

内部変数が2つある場合、zip関数で1つにまとめられたタプルはfor文の中で再び「i」と「j」という変数に分割され、それぞれ単体で1つの値として扱われます。

単純に2つのイテラブルオブジェクトを同時にfor文にかけたい場合は、このようにイテラブルオブジェクトの数だけ内部変数を用意して、個々の値を受け取りましょう。

ループを使わずに型を変換する

zip関数 = forループのイメージが強いですが、2つ以上のイテラブルを単純にまとめるのであればループ処理は必要ありません。ざっと流して見てみましょう。

year = [607,710,794,1336,1467]
event = ["遣隋使","平城京","平安京","室町幕府","応仁の乱"]

この2つのリストを1つのリストにまとめる場合は、

history = list(zip(year,event))
#[(607, '遣隋使'), (710, '平城京'), (794, '平安京'), (1336, '室町幕府'), (1467, '応仁の乱')]

タプルにするには…

history = tuple(zip(year,event))
#((607, '遣隋使'), (710, '平城京'), (794, '平安京'), (1336, '室町幕府'), (1467, '応仁の乱'))

あるいは辞書(dict)に変換。

history = dict(zip(year,event))
#{607: '遣隋使', 710: '平城京', 794: '平安京', 1336: '室町幕府', 1467: '応仁の乱'}

集合(set)でも…

history = set(zip(year,event))
#{(710, '平城京'), (794, '平安京'), (1336, '室町幕府'), (607, '遣隋使'), (1467, '応仁の乱')}

単純にzipオブジェクトを型コンストラクタの引数にするだけでその型に変換できます。ただし辞書型の場合、3つ以上の値は受け付けられないので注意してください。

year = [607,710,794,1336,1467]
event = ["遣隋使","平城京","平安京","室町幕府","応仁の乱"]
key = ["小野妹子","元明天皇","桓武天皇","足利尊氏","足利義政"]

history = dict(zip(year,event,key))
#ValueError: dictionary update sequence element #0 has length 3; 2 is required
値のエラー: 辞書型を作成する要素0番目に3つの値が存在する

dictコンストラクタに渡す最終的な値が2つであれば問題ありません。

history = dict(zip(year,zip(event,key)))
#{607: ('遣隋使', '小野妹子'), 710: ('平城京', '元明天皇'), 794: ('平安京', '桓武天皇'), 1336: ('室町幕府', '足利尊氏'), 1467: ('応仁の乱', '足利義政')}

zip関数のルールとzip_longest

zip関数は「要素数が少ない方のイテラブルの要素が尽きた場合、そこで打ち切る」というルールがあります。

year = [607,710,794,1336,1467]
event = ["遣隋使","平城京","平安京"]

history = []
for i,j in zip(year,event):
    print(j+"は"+str(i)+"年")
#遣隋使は607年
#平城京は710年
#平安京は794年

eventの要素数が少ないため、eventの要素が無くなった時点でループは終了しています。これを「要素数が多い方に合わせる」のであれば、itertoolsモジュールのzip_longest関数をインポートしましょう。

from itertools import zip_longest

year = [607,710,794,1336,1467]
event = ["遣隋使","平城京","平安京"]

history = []
for i,j in zip_longest(year,event):
    print(str(j)+"は"+str(i)+"年")
#遣隋使は607年
#平城京は710年
#平安京は794年
#Noneは1336年
#Noneは1467年

zip関数の代わりにzip_longest関数を使用することで、足りない部分は「None」で補われ、要素数が多い側の要素が無くなるまでループは続けられます。

zipしたものを元に戻す – 展開のアスタリスク

zip関数に複数のイテラブルを渡すと、値の並びを変化させます。

x = (1,2,3)
y = (4,5,6)
z = (7,8,9)

zipped = list(zip(x,y,z))
print(zipped)     #[(1, 4, 7), (2, 5, 8), (3, 6, 9)]

「1,2,3」と並んでいた値はzip関数によって「1,4,7」と縦軸の並びをひとまとめにされました。ではこのzippedを元の「1,2,3」という構造に戻すにはどうすればよいでしょうか?

実は元に戻す作業はzip関数を使うと1行で可能です。

a,b,c = zip(*zipped)
print(a)     #(1, 2, 3)
print(b)     #(4, 5, 6)
print(c)     #(7, 8, 9)

このアスタリスクは「展開(アンパック)のアスタリスク」です。

list(zip(x,y,z))でリストとしてひとまとめにされた変数「zipped」からリストを取り払うと、(1,4,7),(2,5,8),(3,6,9)と3つのタプルに分かれます。

後はこれをzip関数でa,b,c3つの変数に振り分ければ、データは元通りに組み替えられるという仕組みです。

Pythonのアスタリスクについては、こちらの記事が簡潔にまとまっていて素晴らしいと思います。

Python3.xのアスタリスク逆引き - Qiita
前書き意外と簡潔にまとめた記事が無かったので書いた。Python初心者がさっくり理解できることを目的としている。関数を呼び出すときアスタリスク *my_list = p…
タイトルとURLをコピーしました