Python __getitem__ y en el operador resultan en un comportamiento extraño

34

Lo que explica el siguiente comportamiento:

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
fuente

Respuestas:

45

Si un objeto no tiene una __contains__implementación, inrecurre a un valor predeterminado que básicamente funciona así:

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

Y si un objeto no tiene una __iter__implementación, forrecurre a un valor predeterminado que básicamente funciona así:

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

Estos valores predeterminados se utilizan incluso si el objeto no pretende ser una secuencia.

Su 1 in fy las 5 in fpruebas están utilizando los retrocesos predeterminados para iny for, lo que lleva al comportamiento observado. 1 in fencuentra de 1inmediato, pero __getitem__nunca regresa 5, por lo que 5 in fcorre para siempre.

(Bueno, en realidad, en la implementación de referencia de Python, la __iter__reserva predeterminada almacena el índice en una variable de nivel C de tipo Py_ssize_t, por lo que si espera lo suficiente, esa variable se maximiza y Python genera un OverflowError . Si vio eso, usted debe estar en una compilación de Python de 32 bits. Las computadoras no han existido lo suficiente como para que alguien pueda acceder a eso en una Python de 64 bits).

user2357112 es compatible con Monica
fuente
Con respecto al OverflowError, ejecuté esto en 64 y 32 bits, y tiene razón, solo lo vi en 32 bits.
Matthew Moisen
¿Conoces la documentación que explica esto? Me gustaría leer sobre por qué se decidió esta implementación.
Matthew Moisen
3
@Matthew Expressions> Operaciones de prueba de membresía , también objeto .__ contiene__ y el párrafo justo arriba
wjandrea
44
@MatthewMoisen: Estos valores predeterminados fueron el comportamiento original de fory in, anterior a la introducción de __iter__y __contains__. Consulte la documentación de Python 1.4 aquí y aquí .
user2357112 es compatible con Monica el