回答您的問題,我們可以檢查使用dis
模塊的兩個函數生成的字節碼:
In [5]: def no_unpacking():
...: s = []
...: for item in SEQUENCE:
...: s.append(item[0] + item[1])
...: return s
...:
...:
...: def unpacking():
...: s = []
...: for a,b in SEQUENCE:
...: s.append(a+b)
...: return s
我都擴大了列表理解,因爲在python3它會更繁瑣的檢查有趣字節碼。代碼是相同的,所以它對我們的目的無關緊要。
的第一個功能字節碼是:
In [6]: dis.dis(no_unpacking)
2 0 BUILD_LIST 0
3 STORE_FAST 0 (s)
3 6 SETUP_LOOP 39 (to 48)
9 LOAD_GLOBAL 0 (SEQUENCE)
12 GET_ITER
>> 13 FOR_ITER 31 (to 47)
16 STORE_FAST 1 (item)
4 19 LOAD_FAST 0 (s)
22 LOAD_ATTR 1 (append)
25 LOAD_FAST 1 (item)
28 LOAD_CONST 1 (0)
31 BINARY_SUBSCR
32 LOAD_FAST 1 (item)
35 LOAD_CONST 2 (1)
38 BINARY_SUBSCR
39 BINARY_ADD
40 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
43 POP_TOP
44 JUMP_ABSOLUTE 13
>> 47 POP_BLOCK
5 >> 48 LOAD_FAST 0 (s)
51 RETURN_VALUE
注意循環必須調用BINARY_SUBSCR
兩次訪問元組的兩個元素。
第二函數的字節碼:
In [7]: dis.dis(unpacking)
9 0 BUILD_LIST 0
3 STORE_FAST 0 (s)
10 6 SETUP_LOOP 37 (to 46)
9 LOAD_GLOBAL 0 (SEQUENCE)
12 GET_ITER
>> 13 FOR_ITER 29 (to 45)
16 UNPACK_SEQUENCE 2
19 STORE_FAST 1 (a)
22 STORE_FAST 2 (b)
11 25 LOAD_FAST 0 (s)
28 LOAD_ATTR 1 (append)
31 LOAD_FAST 1 (a)
34 LOAD_FAST 2 (b)
37 BINARY_ADD
38 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
41 POP_TOP
42 JUMP_ABSOLUTE 13
>> 45 POP_BLOCK
12 >> 46 LOAD_FAST 0 (s)
49 RETURN_VALUE
注意怎麼沒有BINARY_SUBSCR
執行。
所以,好像UNPACK_SEQUENCE
加一STORE_FAST
(這是由拆包添加的額外操作)更快然後做了兩BINARY_SUBSCR
。 這是合理的,因爲BINARY_SUBSCR
是一個全面的方法調用,而UNPACK_SEQUENCE
和STORE_FAST
是更簡單的操作。
你可以看到區別,即使在簡單的情況:
In [1]: def iter_with_index(s):
...: for i in range(len(s)):
...: s[i]
...:
In [2]: def iter_without_index(s):
...: for el in s:el
...:
In [3]: %%timeit s = 'a' * 10000
...: iter_with_index(s)
...:
1000 loops, best of 3: 583 us per loop
In [4]: %%timeit s = 'a' * 10000
...: iter_without_index(s)
...:
1000 loops, best of 3: 206 us per loop
正如你可以看到遍歷字符串慢3倍左右使用明確的索引。 由於致電BINARY_SUBSCR
,這些都是開銷。
關於第二個問題:pypy的JIT能夠分析代碼並生成優化版本,避免索引操作的開銷。 當它意識到訂閱在元組上完成時,它可能會生成不調用元組方法但直接訪問元素的代碼,從而完全刪除操作。
很好的解釋,很高興知道我並不擔心沒有理由。 – alecxe
很好的回答。很徹底。 – fr1tz
真的很好的解釋。但是,只要將'range'改爲'xrange',筆記本的速度就會從4.0 us降低到3.43 us –