Python __getitem__およびin演算子は奇妙な動作を引き起こします


34

次の動作を説明するもの:

class Foo:
    def __getitem__(self, item):
        print("?")
        return 1

f = Foo()

1 in f  # prints one ? and returns True

5 in f  # prints ? forever until you raise a Keyboard Exception

# Edit: eventually this fails with OverflowError: iter index too large

回答:


45

オブジェクトに__contains__実装がない場合は、in基本的に次のように機能するデフォルトにフォールバックします。

def default__contains__(self, element):
    for thing in self:
        if thing == element:
            return True
    return False

オブジェクトに__iter__実装がない場合は、for基本的に次のように機能するデフォルトにフォールバックします。

def default__iter__(self):
    i = 0
    try:
        while True:
            yield self[i]
            i += 1
    except IndexError:
        pass

これらのデフォルトは、オブジェクトがシーケンスであることを意図していない場合でも使用されます。

あなた1 in f5 in fテストのために、デフォルトのフォールバックを使用しているinfor、観測された行動につながります。1 in f1すぐに見つかりますが、__getitem__戻ることはない5ため、5 in f永久に実行されます。

(まあ、実際には、Pythonのリファレンス実装では、デフォルトの__iter__フォールバックがタイプのCレベルの変数にインデックスを保存するPy_ssize_tため、十分に待機すると、その変数は最大になり、PythonはOverflowErrorを発生させます。それを見た場合、 32ビットのPythonビルド上にある必要があります。64ビットのPythonでコンピュータを攻撃するのに十分な期間、コンピュータは存在していません。


OverflowErrorについては、64ビットと32ビットの両方でこれを実行しましたが、32ビットでしか見られませんでした。
Matthew Moisen

これを説明するドキュメントを知っていますか?なぜこの実装が決定されたのかについてお話ししたいと思います。
Matthew Moisen


4
@MatthewMoisen:これらのデフォルトは、元の行動だったforin、の導入をより以前__iter____contains__こちらこちらの Python 1.4ドキュメントをご覧ください
user2357112は、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.