Skip to content Skip to sidebar Skip to footer

How Is The __contains__ Method Of The List Class In Python Implemented?

Suppose I define the following variables: mode = 'access' allowed_modes = ['access', 'read', 'write'] I currently have a type checking statement which is assert any(mode == allowe

Solution 1:

No, they're not equivalent. For example:

>>> mode = float('nan')
>>> allowed_modes = [mode]
>>> any(mode == allowed_mode for allowed_mode in allowed_modes)
False
>>> mode in allowed_modes
True

See Membership test operations for more details, including this statement:

For container types such as list, tuple, set, frozenset, dict, or collections.deque, the expression x in y is equivalent to any(x is e or x == e for e in y).


Solution 2:

Python lists are defined in C code.

You may verify it by looking at the code in the repository:

static int
list_contains(PyListObject *a, PyObject *el)
{
    Py_ssize_t i;
    int cmp;

    for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i)
        cmp = PyObject_RichCompareBool(el, PyList_GET_ITEM(a, i),
                                           Py_EQ);
    return cmp;
}

It's fairly straight forward to see that this code loops over items in list and stop when first equality (Py_EQ) comparison between el and PyList_GET_ITEM(a, i) returns 1.


Solution 3:

Not equivalent since the any requires an extra function call, a generator expression and things.

>>> mode = "access"
>>> allowed_modes =["access", "read", "write"]
>>> 
>>> def f1():
...    mode in allowed_modes
... 
>>> def f2():
...    any(mode == x for x in allowed_modes)
... 
>>> 
>>> 
>>> import dis
>>> dis.dis
dis.dis(          dis.disassemble(  dis.disco(        dis.distb(        
>>> dis.dis(f1)
  2           0 LOAD_GLOBAL              0 (mode)
              3 LOAD_GLOBAL              1 (allowed_modes)
              6 COMPARE_OP               6 (in)
              9 POP_TOP
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE
>>> dis.dis(f2)
  2           0 LOAD_GLOBAL              0 (any)
              3 LOAD_CONST               1 (<code object <genexpr> at 0x7fb24a957540, file "<stdin>", line 2>)
              6 LOAD_CONST               2 ('f2.<locals>.<genexpr>')
              9 MAKE_FUNCTION            0
             12 LOAD_GLOBAL              1 (allowed_modes)
             15 GET_ITER
             16 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             19 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             22 POP_TOP
             23 LOAD_CONST               0 (None)
             26 RETURN_VALUE
>>> 

This is more instructive than the python source for the methods themselves but here is the source of __contains__ for lists and the loop is in C which will probably be faster than a Python loop.

Some timing numbers confirm this.

>>> import timeit
>>> timeit.timeit(f1)
0.18974408798385412
>>> timeit.timeit(f2)
0.7702703149989247
>>> 

Post a Comment for "How Is The __contains__ Method Of The List Class In Python Implemented?"