Python __getitem__ e no operador resultam em comportamento estranho

34

O que explica o seguinte comportamento:

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
fonte

Respostas:

45

Se um objeto não tiver uma __contains__implementação,in retornará a um padrão que basicamente funciona assim:

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

E se um objeto não tiver uma __iter__implementação,for retornará a um padrão que basicamente funciona assim:

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

Esses padrões são usados ​​mesmo que o objeto não seja uma sequência.

Seus testes 1 in fe 5 in festão usando os fallbacks padrão para ine for, levando ao comportamento observado. 1 in fencontra 1imediatamente, mas seu __getitem__nunca retorna 5, então5 in f corre para sempre.

(Bem, na implementação de referência do Python, o __iter__fallback padrão armazena o índice em uma variável do tipo C-level Py_ssize_t, portanto, se você esperar o suficiente, essa variável atinge o limite máximo e o Python gera um OverflowError . Se você viu isso, deve estar em uma versão Python de 32 bits. Os computadores não existem o suficiente para que alguém possa acessá-la em um Python de 64 bits.)

user2357112 suporta Monica
fonte
Em relação ao OverflowError, eu executei isso em 64 e 32 bits, e você está certo, eu só vi isso em 32 bits.
Matthew Moisen
Você conhece a documentação que explica isso? Gostaria de ler por que essa implementação foi decidida.
Matthew Moisen
3
@Matthew Expressions> Operações de teste de associação , também objeto .__ contains__ e o parágrafo acima dele #
wjandrea
4
@MatthewMoisen: Esses padrões eram o comportamento original de fore in, anteriores à introdução de __iter__e __contains__. Veja a documentação do Python 1.4 aqui e aqui .
user2357112 suporta Monica