Если объект не имеет __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.)