Python __getitem__ i operator powodują dziwne zachowanie

34

Co wyjaśnia następujące zachowanie:

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
źródło

Odpowiedzi:

45

Jeśli obiekt nie ma __contains__implementacji, inwraca do wartości domyślnej, która w zasadzie działa w następujący sposób:

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

A jeśli obiekt nie ma __iter__implementacji, forwraca do wartości domyślnej, która w zasadzie działa w następujący sposób:

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

Te wartości domyślne są używane, nawet jeśli obiekt nie jest sekwencją.

Twoje 1 in fi 5 in ftesty wykorzystują domyślne awarie dla ini for, co prowadzi do zaobserwowanego zachowania. 1 in fznajduje 1natychmiast, ale __getitem__nigdy nie wraca 5, więc 5 in fdziała wiecznie.

(Cóż, w rzeczywistości w referencyjnej implementacji Pythona domyślny system __iter__rezerwowy przechowuje indeks w zmiennej typu C Py_ssize_t, więc jeśli zaczekasz wystarczająco długo, ta zmienna osiągnie maksimum i Python zgłosi błąd OverflowError . Jeśli to zobaczyłeś, możesz musi być w 32-bitowej wersji Pythona. Komputery nie istniały wystarczająco długo, aby ktokolwiek mógł je uruchomić w 64-bitowym Pythonie).

user2357112 obsługuje Monikę
źródło
Jeśli chodzi o błąd przepełnienia, uruchomiłem to zarówno na 64-bitowym, jak i 32-bitowym pliku, i masz rację, widziałem to tylko na 32-bitowym.
Matthew Moisen,
Czy znasz dokumentację, która to wyjaśnia? Chciałbym przeczytać, dlaczego podjęto decyzję o wdrożeniu.
Matthew Moisen
3
@Matthew Expressions> Operacje testowe członkostwa , również obiekt .__
zawiera__
4
@MatthewMoisen: Domyślne były pierwotne zachowanie fori inpoprzedzające wprowadzenie __iter__i __contains__. Zobacz dokumentację Python 1.4 tutaj i tutaj .
użytkownik2357112 obsługuje Monikę