Python, zip関数の使い方: 複数のリストの要素をまとめて取得
Pythonの組み込み関数zip()
は複数のイテラブルオブジェクト(リストやタプルなど)の要素をまとめる関数。forループで複数のリストの要素を取得する際などに使う。
ZIPファイルの圧縮や解凍については以下の記事を参照。
forループで複数のリストの要素を取得
forループの中で複数のイテラブルオブジェクト(リストやタプルなど)の要素を同時に取得して使いたい場合は、zip()
関数の引数にそれらを指定する。
names = ['Alice', 'Bob', 'Charlie']
ages = [24, 50, 18]
for name, age in zip(names, ages):
print(name, age)
# Alice 24
# Bob 50
# Charlie 18
2つだけでなく、3つ以上でも同様。
points = [100, 85, 90]
for name, age, point in zip(names, ages, points):
print(name, age, point)
# Alice 24 100
# Bob 50 85
# Charlie 18 90
要素数が異なる場合の処理
zip()では多い分の要素が無視される
zip()
では、それぞれのリストの要素数が異なる場合、少ない(短い)方の要素数までが返され、多い分は無視される。
names = ['Alice', 'Bob', 'Charlie', 'Dave']
ages = [24, 50, 18]
for name, age in zip(names, ages):
print(name, age)
# Alice 24
# Bob 50
# Charlie 18
zip()で要素数が異なる場合にエラーにする: strict引数(Python3.10以降)
Python3.10でzip()
にstrict
引数が追加された。3.9以前は使えないので注意。
strict=True
とすると要素数が異なる場合にエラーとなる。
# for name, age in zip(names, ages, strict=True):
# print(name, age)
# ValueError: zip() argument 2 is shorter than argument 1
デフォルトはstrict=False
で、3.9までと同じ挙動。少ない方の要素数までが返され、多い分は無視される。
itertools.zip_longest()では足りない分の要素が埋められる
標準ライブラリitertools
モジュールのzip_longest()
を使うと、それぞれのリストの要素数が異なる場合に、足りない要素を任意の値で埋めることができる。
デフォルトではNone
で埋められる。
from itertools import zip_longest
names = ['Alice', 'Bob', 'Charlie', 'Dave']
ages = [24, 50, 18]
for name, age in zip_longest(names, ages):
print(name, age)
# Alice 24
# Bob 50
# Charlie 18
# Dave None
引数fillvalue
を指定するとその値で埋められる。
for name, age in zip_longest(names, ages, fillvalue=20):
print(name, age)
# Alice 24
# Bob 50
# Charlie 18
# Dave 20
要素が足りないリストが複数ある場合も埋める値は一律。別々の値を指定することはできない。
points = [100, 85]
for name, age, point in zip_longest(names, ages, points, fillvalue=20):
print(name, age, point)
# Alice 24 100
# Bob 50 85
# Charlie 18 20
# Dave 20 20
zip_longest()
の中でさらにzip_longest()
を使えば別の値を指定することも可能だが、前もってどのリストの要素が足りないか分かっている必要があるので実用的ではない。
要素数が不明の複数のリストをそれぞれ別の値で埋めたい場合は、
- すべてのリストに対して埋める値を定義しておく
- 最大の要素数を取得する
- すべてのリストを最大の要素数まで埋める
zip()
関数を使う
といった手順が考えられる。
fill_name = 'XXX'
fill_age = 20
fill_point = 50
len_names = len(names)
len_ages = len(ages)
len_points = len(points)
max_len = max(len_names, len_ages, len_points)
names = names + [fill_name] * (max_len - len_names)
ages = ages + [fill_age] * (max_len - len_ages)
points = points + [fill_point] * (max_len - len_points)
print(names)
print(ages)
print(points)
# ['Alice', 'Bob', 'Charlie', 'Dave']
# [24, 50, 18, 20]
# [100, 85, 50, 50]
for name, age, point in zip(names, ages, points):
print(name, age, point)
# Alice 24 100
# Bob 50 85
# Charlie 18 50
# Dave 20 50
最大の要素数まで埋める処理では、任意の値・要素数でのリストの初期化と、+
演算子によるリスト同士の結合を使っている。
これを関数化すると以下のようになる。元のリストとリストを埋める値をそれぞれイテラブル(リストやタプル)で引数に指定するようにしている。
def my_zip_longest(iterables, fillvalues):
max_len = max(len(i) for i in iterables)
return zip(*[list(i) + [v] * (max_len - len(i)) for i, v in zip(iterables, fillvalues)])
for name, age, point in my_zip_longest((names, ages, points), ('XXX', 20, 50)):
print(name, age, point)
# Alice 24 100
# Bob 50 85
# Charlie 18 50
# Dave 20 50
リスト内包表記と*
によるリストの展開を使っている。
- 関連記事: Pythonリスト内包表記の使い方
- 関連記事: Pythonで関数の引数にリスト、タプル、辞書を展開して渡す
複数のイテラブルの要素をタプルにまとめたリストを取得
zip
関数は複数のイテラブルオブジェクトの要素をタプルでまとめたイテレータ(zip
オブジェクト)を返す。forループの外でも使えるし、対象はリストに限定されない。
names = ['Alice', 'Bob', 'Charlie']
ages = (24, 50, 18)
z = zip(names, ages)
print(z)
print(type(z))
# <zip object at 0x1038b0980>
# <class 'zip'>
複数のイテラブルオブジェクトの要素をタプルとしてまとめたリストを取得したい場合は、list()
でリスト化する。
l = list(zip(names, ages))
print(l)
print(type(l))
print(type(l[0]))
# [('Alice', 24), ('Bob', 50), ('Charlie', 18)]
# <class 'list'>
# <class 'tuple'>