In Python, it is known that in checks for membership in iterators (lists, dictionaries, etc) and looks for substrings in strings. My question is regarding how in is implemented to achieve all of the following: 1) test for membership, 2) test for substrings and 3) access to the next element in a for-loop. For example, when for i in myList: or if i in myList: is executed, does in call myList.__next__()? If it does call it, how then does it work with strings, given that str objects are not iterators(as checked in Python 2.7) and so do not have the next() method? If a detailed discussion of in's implementation is not possible, would appreciate if a gist of it is supplied here.
- 8,084
- 8
- 48
- 62
- 545
- 5
- 10
-
1Note: the `in` in `for i in x` is not the same operator as the the `in` in `if i in x`. The first is just syntax/grammar that goes with for, the second is an actual operator. – Max Nov 29 '18 at 16:02
2 Answers
A class can define how the in operator works on instances of that class by defining a __contains__ method.
The Python data model documentation says:
For objects that don’t define
__contains__(), the membership test first tries iteration via__iter__(), then the old sequence iteration protocol via__getitem__(), see this section in the language reference.
Section 6.10.2, "Membership test operations", of the Python language reference has this to say:
The operators
inandnot intest for membership.x in sevaluates toTrueif x is a member of s, andFalseotherwise.x not in sreturns the negation ofx in s. All built-in sequences and set types support this as well as dictionary, for whichintests whether the dictionary has a given key. For container types such as list, tuple, set, frozenset, dict, or collections.deque, the expressionx in yis equivalent toany(x is e or x == e for e in y).For the string and bytes types,
x in yisTrueif and only if x is a substring of y. An equivalent test isy.find(x) != -1. Empty strings are always considered to be a substring of any other string, so"" in "abc"will returnTrue.For user-defined classes which define the
__contains__()method,x in yreturnsTrueify.__contains__(x)returns a true value, andFalseotherwise.For user-defined classes which do not define
__contains__()but do define__iter__(),x in yisTrueif some valuezwithx == zis produced while iterating overy. If an exception is raised during the iteration, it is as ifinraised that exception.Lastly, the old-style iteration protocol is tried: if a class defines
__getitem__(),x in yisTrueif and only if there is a non-negative integer index i such thatx == y[i], and all lower integer indices do not raiseIndexErrorexception. (If any other exception is raised, it is as ifinraised that exception).The operator
not inis defined to have the inverse true value ofin.
As a comment indicates above, the expression operator in is distinct from the keyword in which forms a part of the for statement. In the Python grammar, the in is "hardcoded" as a part of the syntax of for:
for_stmt ::= "for" target_list "in" expression_list ":" suite ["else" ":" suite]
So in the context of a for statement, in doesn't behave as an operator, it's simply a syntactic marker to separate the target_list from the expression_list.
- 59,486
- 16
- 97
- 135
Python has __contains__ special method that is used when you do item in collection.
For example, here's a class that "__contains__" all even numbers:
>>> class EvenNumbers:
... def __contains__(self, item):
... return item % 2 == 0
...
>>> en = EvenNumbers()
>>> 2 in en
True
>>> 3 in en
False
>>>
- 35,554
- 7
- 89
- 134