Якщо об’єкт не має __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.)