Python __getitem__ und in operator führen zu seltsamem Verhalten

34

Was erklärt das folgende Verhalten:

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
Matthew Moisen
quelle

Antworten:

45

Wenn ein Objekt keine __contains__Implementierung hat, wird inauf einen Standard zurückgegriffen, der im Grunde so funktioniert:

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

Und wenn ein Objekt keine __iter__Implementierung hat, greift es forauf einen Standard zurück, der im Grunde so funktioniert:

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

Diese Standardeinstellungen werden auch dann verwendet, wenn das Objekt keine Sequenz sein soll.

Ihre 1 in fund 5 in fTests verwenden die Standard-Fallbacks für inund for, was zu dem beobachteten Verhalten führt. 1 in ffindet 1sofort, aber du kommst __getitem__nie zurück 5, 5 in fläuft also für immer.

(Nun, tatsächlich __iter__speichert der Standard- Fallback bei der Referenzimplementierung von Python den Index in einer Variablen vom Typ C. Py_ssize_tWenn Sie also lange genug warten, wird diese Variable maximal und Python löst einen OverflowError aus . Wenn Sie das gesehen haben, haben Sie muss sich auf einem 32-Bit-Python-Build befinden. Computer haben nicht lange genug existiert, damit jemand dies auf einem 64-Bit-Python erreichen kann.)

user2357112 unterstützt Monica
quelle
In Bezug auf den OverflowError habe ich dies sowohl auf 64- als auch auf 32-Bit ausgeführt, und Sie haben Recht, ich habe es nur auf 32-Bit gesehen.
Matthew Moisen
Kennen Sie zufällig die Dokumentation, die dies erklärt? Ich möchte nachlesen, warum diese Implementierung beschlossen wurde.
Matthew Moisen
3
@ Matthew Expressions> Mitgliedschaftstestoperationen , auch Objekt .__ enthält__ und den Absatz direkt darüber
wjandrea
4
@MatthewMoisen: Diese Standardeinstellungen waren das ursprüngliche Verhalten von forund invor der Einführung von __iter__und __contains__. Lesen Sie hier und hier die Python 1.4-Dokumentation .
user2357112 unterstützt Monica