Programmingバックエンド開発者

Pythonにおける'シーケンス'プロトコルを説明し、それをどのように実装し、単なる反復可能オブジェクトと何が異なるのか?

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

回答。

Pythonにおける'シーケンス'プロトコルは、リストやタプルなどのインデックス付けや反復ができるオブジェクトのためのインターフェースを定義します。シーケンスは、データ構造に対する自然な操作をサポートするために、Pythonの初期バージョンから存在しました:インデックス付け、スライス、要素の反復。

問題 — ユーザ定義クラスがシーケンスのように振る舞うには、__iter__と__next__メソッドだけでは不十分です。シーケンスの動作を完全にサポートするには、追加のメソッドが必要です。

解決策 — 独自のシーケンスタイプを実装するには、getitem__メソッド(インデックス付けやスライスに必要)を定義し、オプションで__len(len()や長さのチェック用)を定義する必要があります。これにより、オブジェクトは反復、インデックスアクセス、スライス、さらにシーケンスに関するPythonの多くの標準操作をサポートします。

コード例:

class MyCounter: def __init__(self, stop): self._stop = stop def __getitem__(self, index): if 0 <= index < self._stop: return index * 10 else: raise IndexError('範囲外です') def __len__(self): return self._stop c = MyCounter(5) print(c[3]) # 30 print(len(c)) # 5 for x in c: print(x)

主な特徴:

  • オブジェクトは__getitem__を通じてインデックスアクセスやスライスをサポートします。
  • len()関数をサポートするためには__len__が必要です。
  • イテレーションは__iter__が定義されていない場合、__getitem__に基づいて構築されます。

トリッキーな質問。

__iter__と__next__のみを実装した場合、私のオブジェクトはシーケンス(Sequence)になりますか?

いいえ。そのようなオブジェクトは反復可能(iterable)ですが、シーケンスにはなりません。インデックス付け、スライス、リストライクオブジェクトの標準関数をサポートしません。

forループをサポートするには__getitem__を実装する必要がありますか?

必ずしも必要ではありません。__iter__が実装されていれば、forは機能します。しかし、__iter__がない場合、インタプリタは__getitem__を使用し、インデックス0から始めてIndexErrorが発生するまで続けます。したがって、シーケンスには__getitem__だけで十分です。

__getitem__をintに対してのみ実装し、sliceには対応しなくてもよいですか?

技術的には、c[0]には機能しますが、c[1:4]のスライスを取得しようとするとエラーが発生します。スライスをサポートするには、__getitem__はslice型のオブジェクトを処理できる必要があります(slice.indicesとisinstance(key, slice)を参照)。

コード例:

class S: def __getitem__(self, idx): if isinstance(idx, slice): return [x for x in range(idx.start or 0, idx.stop or 10, idx.step or 1)] return idx * 2

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

  • オブジェクトが完全なシーケンスになると期待して__iter__のみを定義します。
  • __getitem__を実装しながらもsliceオブジェクトを処理しません。
  • __len__を実装しないため、len(obj)がTypeErrorを引き起こします。

実生活の例

ネガティブケース

__iter__のみを定義したカスタム構造を実装し、スライスとインデックス付けができると思っています。

プラス面:

  • forループで機能し、ジェネレーターをサポートします。

マイナス面:

  • obj[5]やobj[1:3]の構文がサポートされず、エラーになります。
  • len(obj)も機能しません。

ポジティブケース

クラスが__getitem__を実装し、スライスとintインデックスのサポート、および__len__も実装します。

プラス面:

  • スライス、インデックス付け、len、イテレーションなど、すべてのシーケンス操作をサポートします。
  • 標準ライブラリとの統合(例えば、random.choice)も可能です。

マイナス面:

  • スライスの処理を慎重に実装する必要があり、そうしないとスライスを扱う際にバグが発生する可能性があります。