この記事について
この記事は、生成AIを活用した自動生成フローで作成しています。Python公式ドキュメントのzip関数と例外仕様を根拠に、長さの異なるデータを安全な架空例で比較します。検証ステータス:📘 公式仕様確認済み・掲載コードの実機実行は未実施
Pythonのzipは行を黙って捨てる?strict=TrueでCSV突合ミスを見つける
二つの一覧を一行ずつ組み合わせるとき、Pythonのzip()は便利です。ただし、片方が一行短くても、通常は警告せず短い方に合わせて処理を終えます。備品番号と担当部署の一覧を結び付けたつもりが、末尾の備品だけ記録されなかったら、後で原因を追うのは大変です。今回の結論は、両方の行数が同じであるべき業務データにはzip(..., strict=True)を検討することです。Python 3.10以降の機能で、長さが異なるとValueErrorを発生させます。
対応環境と実験の前提
| 項目 | 条件 |
|---|---|
| 対象 | Python標準組み込み関数zip |
| 推奨環境 | Python 3.10以降 |
| データ | 架空の備品番号と部署名 |
| ファイル操作 | なし。メモリ上の一覧のみ |
| 検証範囲 | 公式仕様照合。掲載コードの実機実行は未実施 |
zipは「圧縮ファイルを作るzip形式」とは無関係です。複数の反復可能オブジェクトから値を一つずつ取り出して組にする関数です。たとえば番号が三つ、部署名が二つしかないとき、どのような結果になるかを予想してからコードを見てください。問題はエラーが出るかどうかだけではありません。処理された二行が正しそうに見えるため、欠落に気付きにくいことです。
まず安全な二つの一覧で試す
以下は外部接続もファイル変更も行わない短い例です。結果欄は公式仕様から導いた期待例であり、この記事の制作時点で実行したログではありません。
assets = ["PC-101", "PC-102", "PC-103"]
teams = ["総務", "営業"]
pairs = list(zip(assets, teams))
print(pairs)
print("元の備品数:", len(assets))
print("組み合わせ数:", len(pairs))
期待される組は[('PC-101', '総務'), ('PC-102', '営業')]です。備品は三件あるのに、組み合わせは二件しかありません。末尾のPC-103に対応する部署がないため、その番号は出力から消えます。ここで注目したいのは、Pythonが勝手に空欄を補ったわけではない点です。標準のzipは、いずれかの入力が尽きたところで組を作るのをやめます。
二つのリストの長さを事前にlen()で比較すれば、この例の問題は見つかります。しかし実際のコードでは、リストだけでなく、ファイルから順に読み出すイテレーターや、途中で計算されるジェネレーターを組み合わせることもあります。そうした入力には長さを簡単に問い合わせられない場合があります。単純なlenの比較だけを全ての処理へ流用できるとは限りません。
一条件だけ変える:strict=True
次は入力データを変えず、zipに引数を一つ追加します。データ欠落を許容せず、処理を止めて原因を調べたいときの指定です。
assets = ["PC-101", "PC-102", "PC-103"]
teams = ["総務", "営業"]
try:
pairs = list(zip(assets, teams, strict=True))
print(pairs)
except ValueError as error:
print("入力件数が一致しません:", error)
期待される観察点は、二件の正常な一覧を返す代わりにValueErrorが発生することです。ここでは例外を捕まえてメッセージを表示するため、実験プログラム全体はその場で異常終了しません。業務プログラムでは例外を握りつぶして正常処理へ進むのではなく、入力の確認や再取込みへつなぐ必要があります。つまりstrict=Trueは、不足した部署名を自動的に補修する機能ではありません。欠落が見逃される前に、処理の前提が崩れたことを知らせる機能です。
ここでteamsに"開発"を追加して再実行すれば、双方が三件となり、三組を作れると期待できます。入力を一つ増やした場合と、strictを指定した場合は別の変更です。まずstrictだけを変えて異常を確認し、次に元データを直して正常になることを確認すると、原因と対処を混同しません。
なぜ途中まで組ができるのか
zipは、最初にすべての要素を一覧へ複製してから結合する仕組みではありません。反復時に各入力から次の値を取り出します。片方に次の値がなければ、通常モードではそこで終了します。厳格モードでは、その不一致をエラーとして知らせます。次の図は、同じ二つの一覧に対して設定を変えた場合の判定だけを示します。
flowchart TD
A["二つの一覧から次の値を取得"] --> B{"両方に次の値がある?"}
B -->|はい| C["一組を生成して続行"]
C --> A
B -->|両方とも終了| D["正常終了"]
B -->|片方だけ終了| E{"strict=True?"}
E -->|はい| F["ValueErrorで不一致を通知"]
E -->|いいえ| G["短い方に合わせて終了"]
図の右側にある二つの終了方法が重要です。標準モードの終了は、プログラムが失敗したという意味にはなりません。一方、厳格モードの例外は「何行目のデータが正しいか」を判断した結果でもありません。入力が同じ長さという前提に違反したことだけを伝えます。入力の順番が入れ替わっていても件数が同じなら、この検査だけでは気付きません。
行数が一致しても、対応関係は保証されない
備品番号の一覧と部署名の一覧がどちらも三件なら、strict=Trueでも正常に組を作ります。しかし、部署名の並び順が違っていれば、番号と部署の対応が誤ったまま結合されます。これはzipの不具合ではなく、位置で結合するという設計上の性質です。二つのCSVを独立に並び替えた後で一行ずつ合わせるのは危険です。
たとえば、備品番号と部署が同じ行に含まれる元CSVを読み取れるなら、その関係を保ったまま処理する方が安全です。二つの独立した表を結合する必要があるなら、行番号ではなく共通の一意なキーで対応付ける方式を検討してください。キーの重複、欠損、型の違いも別途検査が必要です。strict=Trueは件数の一致を検査する道具であり、データ内容の整合性検査を全て代替するものではありません。
逆に、二つの系列のうち短い方まで処理できれば十分という用途もあります。たとえば二つの測定値を、両方そろっている範囲だけ比較したい場合です。その場合は標準のzipが仕様に合います。余った値を捨てたくないが、不足分を明示的な値で埋めたい場合はitertools.zip_longestが候補です。ただし埋めた値と本物のデータを取り違えない設計が必要です。
失敗時の切り分け
ValueErrorが出たら、まずどの入力が短いかを確認します。リストなら元の件数を表示し、データ取得前後でレコード数が変わっていないか調べます。CSVならヘッダーを行数へ含めていないか、空行をどこで除外したか、読込み途中で例外が起きていないかを確認します。複数ファイルの更新時刻が違う場合は、異なる世代のデータを組み合わせていないかも重要です。
zipが返すのは反復可能なオブジェクトなので、実際に反復を進めるまで例外が表面化しないことがあります。上の実験でlist(...)を使ったのは、最後まで値を取り出して不一致を観察するためです。単にpairs = zip(assets, teams, strict=True)と代入した時点では、全件検査を終えたことにはなりません。遅延評価の性質を知らないと、「strictを指定したのにエラーが出なかった」と誤解しがちです。
また、片方が無限に値を生成するイテレーターなら、反復を最後まで完了させる設計そのものを見直す必要があります。入力に副作用がある場合、途中まで読み出した状態から再試行すると二重処理になることもあります。単純な架空リストでは安全でも、実務のデータ取得処理にそのまま置き換える前に、入力が再利用できるか、途中停止後の扱いはどうなるかを確認してください。
業務での使い分けと導入手順
担当部署、請求金額、社員番号など、二つの列が必ず同じ件数であるべき場合は厳格モードを優先します。エラーを記録したら、元のデータを修正してから再処理し、不完全な組を正式な台帳へ保存しない運用が適しています。一方、短い側までの計算が業務仕様として認められている場合は、通常モードを使う理由をコードコメントや設計書に残してください。
本番導入では、まず架空データで件数が一致する場合としない場合を別々に試します。次に入力順がずれたケースを用意し、厳格モードだけでは内容の取り違えを検出できないことを確かめます。そのうえで入力件数、処理件数、例外発生時の保存可否をログへ残す設計にします。実データに個人情報が含まれる場合、調査用ログへ行全体を出す必要はありません。件数や匿名化した識別子など、必要最小限の情報だけを残す方が安全です。
最後に、Python 3.9以前ではzipのstrict引数は利用できません。古い実行環境へコードを持ち込むときは、最初にpython --versionなどでバージョンを確認してください。古い環境で同等の確認が必要なら、入力がリストなどで長さを取得できる場合は事前に件数比較する方法があります。ただしそれは反復中の不一致を自動的に検出するstrict=Trueと完全に同じ動作ではありません。
まとめ
zipは便利ですが、標準動作では短い入力に合わせて終わります。だからこそ、処理結果が見た目に正常でも欠落がないとは限りません。strict=Trueを使うと件数不一致を例外にできます。ただし、並び順やキーの正しさまでは確認しません。件数の検査と対応関係の検査を別々に設計することが、業務データを安全に結合するための要点です。
公式資料
Python公式:組み込み関数 zip — https://docs.python.org/3/library/functions.html#zip
Python公式:itertools.zip_longest — https://docs.python.org/3/library/itertools.html#itertools.zip_longest
Python公式:PEP 618(zipのstrict引数) — https://peps.python.org/pep-0618/
