Python __getitem__ et dans l'opérateur entraînent un comportement étrange

34

Ce qui explique le comportement suivant:

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
la source

Réponses:

45

Si un objet n'a pas d' __contains__implémentation, inretombe sur une valeur par défaut qui fonctionne essentiellement comme ceci:

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

Et si un objet n'a pas d' __iter__implémentation, forretombe sur une valeur par défaut qui fonctionne essentiellement comme ceci:

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

Ces valeurs par défaut sont utilisées même si l'objet n'est pas destiné à être une séquence.

Vos tests 1 in fet 5 in futilisent les substitutions par défaut pour inet for, conduisant au comportement observé. 1 in ftrouve 1immédiatement, mais votre __getitem__ne revient jamais 5, donc 5 in ffonctionne pour toujours.

(Eh bien, en fait, sur l'implémentation de référence de Python, le __iter__repli par défaut stocke l'index dans une variable de type C de type Py_ssize_t, donc si vous attendez assez longtemps, cette variable atteint son maximum et Python déclenche une OverflowError . Si vous avez vu cela, vous doit être sur une version Python 32 bits. Les ordinateurs n'existent pas depuis assez longtemps pour que quiconque puisse l'atteindre sur un Python 64 bits.)

user2357112 prend en charge Monica
la source
En ce qui concerne l'OverflowError, je l'ai exécuté à la fois sur 64 et 32 ​​bits, et vous avez raison, je ne l'ai vu que sur 32 bits.
Matthew Moisen
Connaissez-vous la documentation qui explique cela? Je voudrais vous expliquer pourquoi cette mise en œuvre a été décidée.
Matthew Moisen
3
@Matthew Expressions> Opérations de test d'adhésion , également l' objet .__ contient__ et le paragraphe juste au-dessus
wjandrea
4
@MatthewMoisen: Ces valeurs par défaut étaient le comportement d'origine de foret in, antérieures à l'introduction de __iter__et __contains__. Voir la documentation Python 1.4 ici et ici .
user2357112 prend en charge Monica