Programmingバックエンド開発者

Pythonにおける反復可能オブジェクトとは何か、ユーザー定義の反復可能オブジェクトを正しく実装する方法は?

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

回答

問題の歴史: 反復可能性の概念は、Pythonにおいてコレクション(リスト、辞書、集合など)を統一して操作するために登場しました。forループを使用してループ処理できる任意のオブジェクトは、反復可能と見なされます。これは特定の魔法のメソッドを介して実現されています。

問題: Pythonは、シーケンスに関連するループや関数の正しい動作のために特定のプロトコルを要求します。ユーザーが自分のクラスでこれらのプロトコルを正しく実装しないと、標準のメカニズム(for、list()、sum()など)が機能しないか、予期しない動作をすることがあります。

解決策: 反復可能なのは、__iter__メソッドを実装しているオブジェクトです。イテレータは、__next__メソッドとselfを返す__iter__メソッドを持つオブジェクトです。通常、__iter__から返されるオブジェクトはイテレータですが、必ずしもそうではありません。例:

class MyRange: def __init__(self, start, end): self.start = start self.end = end def __iter__(self): self.current = self.start return self def __next__(self): if self.current < self.end: val = self.current self.current += 1 return val raise StopIteration for x in MyRange(1, 4): print(x) # 1, 2, 3

重要な特徴:

  • 反復可能性は、__iter__メソッドの有無によって決まります。
  • イテレータは__next__とselfを返す__iter__の両方を実装する必要があります。
  • __iter__で新しいオブジェクトを返すことで、コレクションに対して複数の独立した巡回を効率的に実現できます。

トリックのある質問。

すべての反復可能オブジェクトに__next__メソッドは必要ですか?

いいえ。反復可能オブジェクトには、イテレータを返す__iter__のみが必要です。__next__は、イテレータ自体が実装します。例えば、listには__next__メソッドがありませんが、反復可能です:その__iter__はイテレータのインスタンスを返します。

lst = [1, 2, 3] print(hasattr(lst, '__next__')) # False

オブジェクトはそれ自体でイテレータになれますか?

はい、両方のメソッド__iter____next__を実装している場合はそうです。

1つのコレクションに対して独立した状態の複数のイテレータを作成できますか?

はい、__iter__が毎回新しいイテレータオブジェクトを返す場合は可能です。

class MyList: def __init__(self, data): self.data = data def __iter__(self): return iter(self.data)

よくある間違いやアンチパターン

  • __iter__なしで__next__を実装する(またはその逆)。
  • クラスレベルで状態を保持することにより、インスタンスレベルではなく、再度巡回する際にバグが生じます。

実生活の例

ネガティブケース: イテレータクラスが状態(例えば、現在のインデックス)をクラスレベルで保持し、インスタンスレベルではないため、並行して巡回時に互いに壊れてしまいます。 利点:

  • メモリが少なくて済みます(インデックスが1つだけ)。 欠点:
  • 2つのループで同時にイテレートする際のエラー。

ポジティブケース: 各イテレータは、__iter__で作成されたインスタンスにその状態を保持します。 利点:

  • 複数のイテレータが正しく動作します。 欠点:
  • メモリ使用量がわずかに増加します。