假设我定义了以下变量:
mode = "access"
allowed_modes = ["access", "read", "write"]
我目前有一个类型检查语句
assert any(mode == allowed_mode for allowed_mode in allowed_modes)
然而,似乎我可以用
简单地替换它assert mode in allowed_modes
根据ThiefMaster中Python List Class __contains__ Method Functionality的答案,这两者应该是等价的。确实如此吗?我怎样才能通过查找Python的源代码轻松验证这一点?
答案 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_EQ
和el
之间的第一次相等(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
>>>