Python で2つの配列をfor 文で扱いたい場合によく使うのが zip() です。

zip()を使った for 文では暗黙的に同じ大きさが要求されると思っていたが、実際には以下のように2つの配列の大きさが異なっていてもエラーが出ないことに気が付かず、困ったことがあった。

In [1]: a = [1,2,3,4]
 
In [2]: b = [1,2,3]
 
In [3]: for i,j in zip(a,b):
   ...:     print(i,j)
   ...:
1 1
2 2
3 3

てっきり、大きい配列の要素を参照時にエラーが発生するかと思ったら、そんなことはなかった。

assert とかで事前にコケるようにしておくとか必要そう。 もしくは、両者の配列のサイズが同じことを明示的に確認するのが吉。

また蛇足だが、Stackoverflow では意図的に異なる大きさの配列を上手く循環させつつ回したい場合の対処法も書いてあり勉強になった。1

2022-10-27 追記 @ftnext さんから以下の情報2を教えてもらいました。

小さい方を読み切ったらforを抜けるの予想と違いますよね。 3.10からzipにstrict引数が追加されており、Trueを指定すれば長さが異なるとValueErrorを送出するようになったんです! https://docs.python.org/ja/3/library/functions.html#zip… また長い方に合わせたいときはzip_longestが標準ライブラリのitertoolsにありますー

なるほど。

  • Without the strict=True argument, any bug that results in iterables of different lengths will be silenced, possibly manifesting as a hard-to-find bug in another part of the program.

わかる〜〜ということで試してみました。

In [1]: a = [1,2,3,4]
In [2]: b = [1,2,3]
In [3]: for i,j in zip(a,b,strict=True):
   ...:     print(i,j)
   ...: 
1 1
2 2
3 3
-------------------------------------------------------------------------
ValueError                              Traceback (most recent call last)
Cell In [3], line 1
----> 1 for i,j in zip(a,b,strict=True):
      2     print(i,j)
 
ValueError: zip() argument 2 is shorter than argument 1

おぉ、これは便利ですね!

結論

Python で zip関数を使う場合、2つの配列に同じ大きさを想定する場合は strict=True を使うとバグの温床を潰せる

Footnotes

  1. How to zip two differently sized lists, repeating the shorter list? - Stack Overflow

  2. https://twitter.com/ftnext/status/1583483767645011968