运算符中的Python - 短路

时间:2018-01-04 22:23:19

标签: python python-internals short-circuiting

我正在Short-Circuiting in Python上阅读一篇有趣的帖子,并想知道in运算符是否属实。我的简单测试将得出结论:它没有:

%%timeit -n 1000
0 in list(range(10))
1000 loops, best of 3: 639 ns per loop

%%timeit -n 1000
0 in list(range(1000))
1000 loops, best of 3: 23.7 µs per loop
# larger the list, the longer it takes. however, i do notice that a higher 
# value does take longer.

%%timeit -n 1000
999 in list(range(1000))
1000 loops, best of 3: 45.1 µs per loop

是否详细解释了为什么999需要的时间超过0in运算符是否像循环一样?

另外,有没有办法告诉in运营商"停止循环"一旦找到价值(或者这是我没见过的已经违约的行为)?

最后 - 是否还有另一个我正在跳过的操作员/功能,这就是我所说的关于"短路" in

3 个答案:

答案 0 :(得分:3)

确实发生了短路。 in运算符调用__contains__方法,而每个类的实现方式不同(在您的情况下为list)。搜索999需要大约两倍的时间来搜索0,因为一半的工作正在创建列表,而另一半正在迭代它,在{{0的情况下会被短路。 1}}。

答案 1 :(得分:2)

list_contains中可以找到inlist对象的实现。它执行列表扫描并在最后一次比较找到元素时提前退出,继续那里没有意义。

涉及的循环是:

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

如果cmp1(匹配时从PyObject_RichCompareBool返回的值),则for循环条件(cmp == 0 && i < Py_SIZE(a))将变为false并终止。

对于内置的列表对象,in的调用是C函数(对于CPython)。对于Python的其他实现,这可以是使用不同语言结构的不同语言。

对于Python中的用户定义类,在参考手册的Membership test operations中定义了所调用的内容,看看那里有一个被调用的内容。

你也可以通过计时来得出这个结论:

l = [*range(1000)]    
%timeit 1 in l
85.8 ns ± 11.9 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit 999 in l
22 µs ± 221 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

元素越远,您需要扫描的越多。如果它没有短路,则所有in操作都会产生类似的时序。

答案 2 :(得分:0)

这是使用散列对象set的另一种外观:

from time import time

qlist = list(range(1000))
qset = set(qlist)

start = time()
for i in range(1000):
    0 in qlist
print time() - start

start = time()
for i in range(1000):
    999 in qlist
print time() - start

start = time()
for i in range(1000):
    0 in qset
print time() - start

start = time()
for i in range(1000):
    999 in qset
print time() - start

输出:

0.000172853469849    0 in list
0.0399038791656    999 in list
0.000147104263306i   0 in set
0.000195980072021  999 in set

正如其他人所说,list实施必须进行顺序搜索。设置包含使用散列值,与查找第一个元素中的项目相同。