2014-09-02 55 views
4

我已經運行在Python 2.7.6以下的小測試:蟒蛇2.7寫作 「x合組」 與 「設置.__包含__(x)的」

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。我的問題是:「爲什麼會有性能差別?」中,當使用s.__contains__(i)你需要做在Python屬性查找,以及使Python中的方法調用Python是如何執行的s.__contains__(i)i in s

回答

10

電話或許有些解釋。這些被作爲單獨的字節代碼執行:

>>> 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__是由一些距離比較慢。

+0

對random.randrange()的調用使您的timeit測試淹沒了十倍。 – msw 2014-09-02 15:37:04

+0

@msw:'random.randrange()'調用*不會在測試*之間變化。我們在這裏比較相對時間。如果您願意,您可以預先生成這些隨機數字,但不會改變結果。 – 2014-09-02 15:43:31

+0

[結果是相反的](http://nbviewer.ipython.org/gist/zed/af2b99517838061ec7b9)在我的機器上 – jfs 2014-09-02 15:51:50