How is the __contains__ method of the list class in Python implemented?

前端 未结 3 853
感动是毒
感动是毒 2020-12-19 12:30

Suppose I define the following variables:

mode = \"access\"
allowed_modes = [\"access\", \"read\", \"write\"]

I currently have a type check

相关标签:
3条回答
  • 2020-12-19 12:40

    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).

    0 讨论(0)
  • 2020-12-19 12:46

    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
    >>> 
    
    0 讨论(0)
  • 2020-12-19 12:47

    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.

    0 讨论(0)
提交回复
热议问题