电话或许有些解释。这些被作为单独的字节代码执行:
>>> 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__
是由一些距离比较慢。
对random.randrange()的调用使您的timeit测试淹没了十倍。 – msw 2014-09-02 15:37:04
@msw:'random.randrange()'调用*不会在测试*之间变化。我们在这里比较相对时间。如果您愿意,您可以预先生成这些随机数字,但不会改变结果。 – 2014-09-02 15:43:31
[结果是相反的](http://nbviewer.ipython.org/gist/zed/af2b99517838061ec7b9)在我的机器上 – jfs 2014-09-02 15:51:50