ProgrammingミドルPython開発者

Pythonにおけるユーザーオブジェクトのための'in'演算子はどのように機能しますか? 'x in your_obj'という表現が機能するために、クラス内で何を実装する必要がありますか?パフォーマンスの問題や予期しないエラーを回避するにはどうすればよいですか?

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

答え。

Pythonの'in'演算子は、要素がコレクションに含まれているかどうかを判断します。ユーザーオブジェクトの場合、x in your_obj構文をサポートするためには、メソッド__contains__を実装する必要があります。これがない場合、インタープリターは__iter__または__getitem__を使用してオブジェクトを反復しようとしますが、動作と効率は異なる可能性があります。

例:

class MyBag: def __init__(self, items): self.items = items def __contains__(self, value): return value in self.items bag = MyBag([1,2,3]) print(2 in bag) # True print(5 in bag) # False

もし__iter__(または__getitem__)だけを実装した場合でも、inは動作しますが、効率は低く、時には期待通りに動作しないことがあります。

注意: コレクションが非常に大きい場合、チェックが単純に実装されていると(例えば、リスト全体をループすることによって)、パフォーマンスの問題が発生する可能性があります。迅速なチェックのためには、例えば集合を使用します。

意外な質問。

'in'演算子の正しい動作のために、__iter__または__getitem__だけを実装すれば十分ですか?動作はどのように変わりますか?

答え:

  • __contains__がない場合、Pythonは(存在する場合)__iter__を使って要素を反復しようとします、または__getitem__(インデックス0から開始し、IndexErrorが発生するまで)を使います。
  • このような動作は効率が低く、メソッドがタイプミスで実装されている場合、無限ループや例外を引き起こす可能性があります。

例:

class Weird: def __getitem__(self, idx): if idx < 3: return idx raise IndexError w = Weird() print(2 in w) # True print(5 in w) # False

このテーマの細かい知識がないために起きた実際のエラーの例。


物語

あるプロジェクトで、エンティティを格納するためのユーザー定義コンテナは、__iter__のみをオーバーライドし、__contains__を実装するのを忘れました。'in'演算子は遅く機能するようになり(大きなコレクションではラグが目立つ)、イテレータが誤ってStopIterationタイプではない例外をスローする際に突然エラーが発生しました。


物語

要素がインデックスで「その場で」計算されるクラスのために、開発者は__getitem__のみを実装しました。大きなxでx in objをチェックしようとすると、長いループが発生し、Out Of Memoryが発生しました。なぜなら、inはIndexErrorに遭遇するまで増加するすべてのインデックスをチェックするためです。


物語

あるプロジェクトでは、'in'に関しては__iter__のみに依存するカスタム辞書が実装されました。これにより、100,000のキーに対する検索が、標準のdict(ここでは__contains__が効率的に実装されている)に対してミリ秒ではなく秒を費やしました。