ProgrammingPythonライブラリ開発者

__getitem__メソッドはPythonでどのように機能し、なぜ実装する必要があり、スライス(slice)を処理する際に考慮すべきことは何ですか?

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

回答。

問題の歴史

__getitem__メソッドは、シーケンスとマッピングのプロトコルの一部としてPythonに追加されました。このマジックメソッドを使用することで、標準コレクション(list、tuple、dictなど)はインデックス、キー、スライスによるアクセスをサポートします。ユーザ定義のクラスでは、このメソッドがオブジェクトを「コレクションのように」振る舞わせることを可能にします。

問題

__getitem__を実装しない場合、ユーザ定義のクラスはインデクシングとforループでの反復をサポートしません。単純なインデックスのみに対して実装を行うことは、スライス(slice)に対して完全な機能を望む場合には適用できず、インデックスのバリエーションを無視することはエラーに繋がります。

解決策

__getitem__を実装してインデックスとスライスタイプのオブジェクトを個別に処理することです。これにより、ユーザオブジェクトを標準的なPython構文で使用できるようになります(たとえば、your_obj[1:5]のスライスをサポートするため)。

コード例:

class MyRange: def __init__(self, n): self.n = n def __getitem__(self, item): if isinstance(item, int): # 個別のインデックス if 0 <= item < self.n: return item * 2 raise IndexError('index out of range') elif isinstance(item, slice): # スライス return [self[i] for i in range(*item.indices(self.n))] else: raise TypeError('Invalid argument type: {}'.format(type(item))) mr = MyRange(10) print(mr[3]) # 6 print(mr[2:5]) # [4, 6, 8]

重要な特徴:

  • __getitem__はシーケンスとイテラブルのプロトコルの基本です
  • スライスをサポートするためにはsliceを個別に処理する必要があります
  • IndexErrorおよびTypeErrorの例外は、標準関数(たとえば、list()、forなど)の正しい動作に必要です

知識を試す質問。

__getitem__の実装だけでインデックスによる値の代入を可能にしますか?

いいえ。代入をサポートするには(your_obj[i] = value)、__setitem__を実装する必要があります。__getitem__は読み取りのみを担当します。

__getitem__は必ずスライスの場合にリストを返す必要がありますか?

いいえ。主な目的は、クラスの意味を考慮して「シーケンス」を返すことです(同じ型を返すか、たとえばtupleを返すことができます)。コンテキストに応じて意味がある限り、それは重要です。

なぜ時々 'MyClass'オブジェクトはサブスクリプト化できないというTypeErrorがありますか?

これは、my_obj[0]を実行しようとしたときに、クラスが__getitem__を実装していない場合に表示されます。クラスがサブスクリプト可能([]をサポートする)であるためには、このメソッドが必須です。

一般的な誤りとアンチパターン

  • intのみをチェックし、sliceを無視する: スライスは常にエラーを引き起こす
  • 不正な型を返す(たとえば、スライスの場合にシーケンスの代わりにNoneを返す)
  • IndexErrorを発生させないため、forループが正常に動作しない

現実の例

ネガティブケース

__getitem__をintのみに対して実装し、sliceを忘れました。my_obj[2:5]の試行はTypeErrorを引き起こし、コレクション処理の全体アルゴリズムが失敗します。

利点:

  • シンプル

欠点:

  • スライスがサポートされず、コードがほとんどの標準構文と互換性がない

ポジティブケース

__getitem__がスライスとintを個別に処理するように実装されています。スライスとインデックスが機能し、list()、map()、イテレーションのメソッドが追加の努力なしにサポートされます。

利点:

  • クラスがPythonの標準ツールと互換性があります
  • 任意のインデックスタイプ(tuple、strなどの行列用)に簡単に拡張できます

欠点:

  • 実装には少し多くのコードとテストが必要です