我在python 2.7.6中运行了以下小测试:
s = set(xrange(0, 1000000))
for i in xrange(0, 5000000):
if s.__contains__(i):
pass
并获得以下输出以运行time python py.py
:
real 0m0.616s
然后我将代码更改为:
s = set(xrange(0, 1000000))
for i in xrange(0, 5000000):
if i in s:
pass
并获得0.467s
的运行时间。我对dict
也得到了相同的结果。我的问题是"为什么存在性能差异?",也许是python如何执行s.__contains__(i)
和i in s
答案 0 :(得分:10)
使用s.__contains__(i)
时,您需要在Python中进行属性查找,以及在Python中进行方法调用。它们作为单独的字节码执行:
>>> import dis
>>> dis.dis(compile('s.__contains__(i)', '<>', 'exec'))
1 0 LOAD_NAME 0 (s)
3 LOAD_ATTR 1 (__contains__)
6 LOAD_NAME 2 (i)
9 CALL_FUNCTION 1
12 POP_TOP
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
LOAD_ATTR
加载__contains__
属性,然后CALL_FUNCTION
执行该方法。
使用i in s
只需一个字节码:
>>> dis.dis(compile('i in s', '<>', 'exec'))
1 0 LOAD_NAME 0 (i)
3 LOAD_NAME 1 (s)
6 COMPARE_OP 6 (in)
9 POP_TOP
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
此处COMPARE_OP
完成所有工作。
在C中,然后查找__contains__
槽(或者更确切地说,它的C等价物)并调用它更快。
请注意,在比较Python中的两种方法时,最好使用timeit
module而不是UNIX time
命令;它会尝试消除环境因素并为您重复测试:
>>> import timeit, random
>>> testset = {random.randrange(50000) for _ in xrange(1000)}
>>> tests = [random.randrange(5000) for _ in xrange(500)]
>>> timeit.timeit('for i in tests: i in s',
... 'from __main__ import testset as s, tests',
... number=100000)
2.5375568866729736
>>> timeit.timeit('for i in tests: s.__contains__(i)',
... 'from __main__ import testset as s, tests',
... number=100000)
4.208703994750977
使用__contains__
会慢一些距离。