Search code examples
pythonmembership

How does python's `in` function work when it triggers via `__getitem__()`'?


The official documentation indicates that python first tries to check via __contains__(), then __iter__(), and finally __getitem__(), depending on which function is defined, in order to resolve an in call. Eg:

if y in x:
    print("y present in x")
else:
    print("y not present in x")

The linked documentation indicates that if there exists any non negative index i such that x[i] == y, then the result is True else False. How does it perform a search over all such i? A linear traversal over 'all' positive numbers seems out of the question. There must be some bounds in which the linear traversal happens (for lists, it should be 0 to len()). How are those bounds determined?


Solution

  • Aaah, I think get it... You want to know how the key is obtained to iterate over all elements on custom containers that do not have neither __contains__() nor __iter__() - simple, it works exactly using linear iteration until IndexError is encountered, as stated in the documentation:

    ... if a class defines __getitem__(), x in y is True if and only if there is a non-negative integer index i such that x == y[i], and all lower integer indices do not raise IndexError exception. (If any other exception is raised, it is as if in raised that exception).

    Case in point:

    class CustomClass(object):
    
        def __getitem__(self, item):
            if item > 20:  # lets force the break so that it doesn't go to sys.maxsize
                raise IndexError()
            print(item)  # print the item requested
            # implied: return None so it doesn't match 5
    
    result = 5 in CustomClass()  # this will keep printing numbers until 20