Programmingバックエンド開発者

Pythonの組み込み関数zip()はどのように機能し、どのように使用されるのか、また異なる長さのシーケンスを処理する際の注意点は何ですか?

Hintsage AIアシスタントで面接を突破

回答。

問題の歴史

zip()関数はPython 2で登場しました(当時はリストを返していました)が、Python 3では遅延イテレータを返します。これは、複数のシーケンスを要素ごとに組み合わせることができ、並行して反復可能なコレクションを処理するのに便利かつ効率的です。

問題

複数のリスト(または他のタイプのシーケンス)を同時に処理する必要があることがよくあります。例えば、キーと値のペアや、点とペアの座標を処理する場合です。インデックスの手動同期はエラーと可読性の低下を引き起こす原因となります。特に異なる長さのコレクションの場合はなおさらです。

解決策

zip()関数は任意の数の反復可能なオブジェクトを受け取り、各反復可能なオブジェクトから対応する要素を含むタプルのイテレータを返します。長さが異なるシーケンスの場合、結果は最も短いシーケンスで切り捨てられます。

コード例:

names = ['Alice', 'Bob', 'Charlie'] ages = [24, 27, 30] for name, age in zip(names, ages): print(f'{name}{age}歳です')

zipは*を使って展開できます:

pairs = [(1, 'a'), (2, 'b'), (3, 'c')] nums, chars = zip(*pairs) print(nums) # (1, 2, 3) print(chars) # ('a', 'b', 'c')

主なポイント:

  • zip()はイテレータを返します(Python 3ではリストではありません)。
  • zip()の動作は最も短いイテレータで停止します。
  • 明示的なインデックス管理なしにコレクションを並行して処理することができます。

ひねりのある質問。

異なる長さのコレクションをzip()に渡すとどうなりますか?

zip()は最も短いコレクションの終わりに達すると停止します — 長いコレクションの残りの要素は無視されます。

print(list(zip([1,2,3], ['a','b']))) # [(1, 'a'), (2, 'b')]

デフォルト値で短いシーケンスを補完するタプルを取得するにはどうすればよいですか?

標準のzip()ではこれを行えませんが、そのような動作をするitertools.zip_longestがあります:

from itertools import zip_longest for a, b in zip_longest([1,2], ['x','y','z'], fillvalue=None): print(a, b) # 1 x # 2 y # None z

zip()の結果を「アンパック」して元のリストを再取得できますか?

はい、すべての元のコレクションの長さが同じであり、結果が変更されていない場合は、*演算子を使用してzipを展開できます。

pairs = [(1,2), (3,4)] a, b = zip(*pairs) print(a) # (1, 3) print(b) # (2, 4)

一般的なエラーとアンチパターン

  • zip()が常に最も長いコレクションの終わりまで「行く」と期待する。
  • Python 3でzip()がリストを返すと仮定する(これはイテレータであり、時にはlist()でラップする必要があります)。
  • 反復するたびに消費される可変ソースでzipを使用する。

実例

ネガティブケース

異なる長さのリンクされたコレクションをzipの特性を考慮せずに処理:

lst1 = [1,2,3,4] lst2 = ['a','b'] for x, y in zip(lst1, lst2): print(x, y) # 1 a # 2 b # (3,4)および'lst1'の'c'、'd'は処理されていません

利点:

  • シーケンスの長さが確実に同じであれば、シンプルで理解しやすいです。

欠点:

  • コレクションの実際の長さが異なる場合、値が失われることがあります。

ポジティブケース

fillvalueを使用してzip_longestを使用し、いかなる要素も失わないようにします:

from itertools import zip_longest lst1 = [1,2,3,4] lst2 = ['a','b'] for x, y in zip_longest(lst1, lst2, fillvalue='?'): print(x, y) # 1 a # 2 b # 3 ? # 4 ?

利点:

  • すべての要素の処理が保証されます。
  • 明示的に「空」の値を指定できます。

欠点:

  • 外部モジュールをインポートする必要があります。
  • fillvalueを忘れないことが重要です。さもなくば、デフォルトでNoneになります。