Если объект не имеет __contains__
реализации, in
возвращается значение по умолчанию, которое в основном работает так:
def default__contains__(self, element):
for thing in self:
if thing == element:
return True
return False
И если объект не имеет __iter__
реализации, for
возвращается значение по умолчанию, которое в основном работает так:
def default__iter__(self):
i = 0
try:
while True:
yield self[i]
i += 1
except IndexError:
pass
Эти значения по умолчанию используются, даже если объект не предназначен для последовательности.
Ваш 1 in f
и 5 in f
тесты используют запасные варианты по умолчанию для in
и for
, что приводит к наблюдаемому поведению. 1 in f
находит 1
сразу, но ваш __getitem__
никогда не возвращается 5
, поэтому 5 in f
работает вечно.
(Ну, на самом деле, в эталонной реализации Python __iter__
резервный вариант по умолчанию хранит индекс в переменной типа C-типа Py_ssize_t
, поэтому, если вы будете ждать достаточно долго, эта переменная будет исчерпана, а Python вызовет OverflowError . Если вы это увидели, вы должен быть в 32-битной сборке Python. Компьютеров не было достаточно долго, чтобы кто-нибудь смог запустить его на 64-битной Python.)
for
иin
, предшествовавшим введению__iter__
и__contains__
. Смотрите документацию по Python 1.4 здесь и здесь .