python 2.7写“x in set”vs“set .__ contains __(x)”

时间:2014-09-02 15:09:27

标签: python performance python-2.7 set

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

的调用的一些解释

1 个答案:

答案 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__会慢一些距离。