Python中列表类的__contains__方法是如何实现的?

时间:2017-01-31 11:48:20

标签: python

假设我定义了以下变量:

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

我目前有一个类型检查语句

assert any(mode == allowed_mode for allowed_mode in allowed_modes)

然而,似乎我可以用

简单地替换它
assert mode in allowed_modes

根据ThiefMasterPython List Class __contains__ Method Functionality的答案,这两者应该是等价的。确实如此吗?我怎样才能通过查找Python的源代码轻松验证这一点?

3 个答案:

答案 0 :(得分:8)

不,他们并不等同。例如:

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

有关详细信息,请参阅Membership test operations,包括此声明:

  

对于容器类型,例如list,tuple,set,frozenset,dict或collections.deque,表达式x in y等同于any(x is e or x == e for e in y)

答案 1 :(得分:7)

Python列表在C代码中定义。

您可以查看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;
}

相当直接地看到此代码循环遍历列表中的项目,并在Py_EQel之间的第一次相等(PyList_GET_ITEM(a, i))比较返回时停止。< / p>

答案 2 :(得分:5)

不等同,因为any需要额外的函数调用,生成器表达式和东西。

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

这比方法本身的python源更有启发性,但here是列表__contains__的来源,循环在C中,可能比Python循环更快。

一些时间数字证实了这一点。

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