rgk
rgk

Reputation: 866

Difference between the in keyword and __contains__ in Python

I was wondering if some one could explain the difference between the "in" keyword of Python and the contains method

I was working with a sample list and found this behavior. When are the two supposed to be used? Is there some efficiency that can be achieved if I use one over the other.

    >>> my_list = ["a", "b", "c"]
    >>> my_list.__contains__("a")
    True
    >>> "a" in my_list
    True

Upvotes: 5

Views: 9251

Answers (4)

Ashwini Chaudhary
Ashwini Chaudhary

Reputation: 250981

Doing "a" in my_list actually calls __contains__ method of my_list if defined.

If __contains__ is not defined then __iter__ is used, if that's not defined then __getitem__ is used.

__contains__ is great way for some containers or iterables to implement fast membership check without looping through the whole sequence, for example range():

>>> %timeit 10**5 in range(10**9)
159 ns ± 1.16 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

https://docs.python.org/3/reference/expressions.html#membership-test-operations

For user-defined classes which define the __contains__() method, x in y returns True if y.__contains__(x) returns a true value, and False otherwise.

For user-defined classes which do not define __contains__() but do define __iter__(), x in y is True if some value z, for which the expression x is z or x == z is true, is produced while iterating over y. If an exception is raised during the iteration, it is as if in raised that exception.

Lastly, the old-style iteration protocol is tried: 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 is y[i] or x == y[i], and no lower integer index raises the IndexError exception. (If any other exception is raised, it is as if in raised that exception).

Upvotes: 1

Anthon
Anthon

Reputation: 76634

The __contains__() method of an an object is called when you use the in statement.

For lists this is pre-defined, but you can also define your own class, add a __contains__ method and use in on the instances of that class.

You should be using in and not call __contains__() directly.

Upvotes: 8

HennyH
HennyH

Reputation: 7944

From the docs:

For the list and tuple types, x in y is true if and only if there exists an index i such that x == y[i] is true.

string types, x in y is true if and only if x is a substring of y. An equivalent test is y.find(x) != -1.

For user-defined classes which define the __contains__() method, x in y is true if and only if y.__contains__(x) is true.

For user-defined classes which do not define __contains__() but do define __iter__(), x in y is true if some value z with x == z is produced while iterating over y. If an exception is raised during the iteration, it is as if in raised that exception.

Lastly, the old-style iteration protocol is tried: 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.

Upvotes: 5

BrenBarn
BrenBarn

Reputation: 251408

Like most magic methods, the __contains__ method is not meant to be called directly. The reason __contains__ exists is precisely so that you can write obj in container instead of having to use method-call syntax. So you should use obj in container.

Upvotes: 2

Related Questions