![](/img/trans.png)
[英]What is the efficient way to write code where argument with same value is passed to a function that is called multiple times ? (python)
[英]Python: Efficient way to call inbuilt function multiple times?
我有一個看起來像這樣的代碼:
def somefunction(somelist):
for item in somelist:
if len(item) > 10:
do something
elif len(item) > 6:
do something
elif len(item) > 3:
do something
else:
do something
由於我多次調用len(item),以這種方式執行效率低下嗎? 編寫下面的代碼還是更好,還是在性能上完全一樣?
def somefunction(somelist):
for item in somelist:
x = len(item)
if x > 10:
do something
elif x > 6:
do something
elif x > 3:
do something
else:
do something
len()是O(1)運算。 這意味着調用len()的成本非常便宜。 因此,不必再為此擔心,可以更好地改進代碼的其他部分。
但是,就我個人而言,我認為第二種方法更好。 因為如果我將變量名從x
更改為length
,它將增加代碼的可讀性。
def somefunction(somelist):
for item in somelist:
length = len(item)
if length > 10:
do something
elif length > 6:
do something
elif length > 3:
do something
else:
do something
注意: len( )
是O(1),帶有字符串,集合和字典。
第二種方法肯定更好,因為減少了對len()
的調用次數:
In [16]: import dis
In [18]: lis=["a"*10000,"b"*10000,"c"*10000]*1000
In [19]: def first():
for item in lis:
if len(item)<100:
pass
elif 100<len(item)<200:
pass
elif 300<len(item)<400:
pass
....:
In [20]: def second():
for item in lis:
x=len(item)
if x<100:
pass
elif 100<x<200:
pass
elif 300<x<400:
pass
....:
您始終可以使用timeit
模塊為代碼計時:
In [21]: %timeit first()
100 loops, best of 3: 2.03 ms per loop
In [22]: %timeit second()
1000 loops, best of 3: 1.66 ms per loop
使用dis.dis()
查看將Python字節碼反匯編為助記符
In [24]: dis.dis(first)
2 0 SETUP_LOOP 109 (to 112)
3 LOAD_GLOBAL 0 (lis)
6 GET_ITER
>> 7 FOR_ITER 101 (to 111)
10 STORE_FAST 0 (item)
3 13 LOAD_GLOBAL 1 (len)
16 LOAD_FAST 0 (item)
19 CALL_FUNCTION 1
22 LOAD_CONST 1 (100)
25 COMPARE_OP 0 (<)
28 POP_JUMP_IF_FALSE 34
4 31 JUMP_ABSOLUTE 7
5 >> 34 LOAD_CONST 1 (100)
37 LOAD_GLOBAL 1 (len)
40 LOAD_FAST 0 (item)
43 CALL_FUNCTION 1
46 DUP_TOP
47 ROT_THREE
48 COMPARE_OP 0 (<)
51 JUMP_IF_FALSE_OR_POP 63
54 LOAD_CONST 2 (200)
57 COMPARE_OP 0 (<)
60 JUMP_FORWARD 2 (to 65)
>> 63 ROT_TWO
64 POP_TOP
>> 65 POP_JUMP_IF_FALSE 71
6 68 JUMP_ABSOLUTE 7
7 >> 71 LOAD_CONST 3 (300)
74 LOAD_GLOBAL 1 (len)
77 LOAD_FAST 0 (item)
80 CALL_FUNCTION 1
83 DUP_TOP
84 ROT_THREE
85 COMPARE_OP 0 (<)
88 JUMP_IF_FALSE_OR_POP 100
91 LOAD_CONST 4 (400)
94 COMPARE_OP 0 (<)
97 JUMP_FORWARD 2 (to 102)
>> 100 ROT_TWO
101 POP_TOP
>> 102 POP_JUMP_IF_FALSE 7
8 105 JUMP_ABSOLUTE 7
108 JUMP_ABSOLUTE 7
>> 111 POP_BLOCK
>> 112 LOAD_CONST 0 (None)
115 RETURN_VALUE
In [25]: dis.dis(second)
2 0 SETUP_LOOP 103 (to 106)
3 LOAD_GLOBAL 0 (lis)
6 GET_ITER
>> 7 FOR_ITER 95 (to 105)
10 STORE_FAST 0 (item)
3 13 LOAD_GLOBAL 1 (len)
16 LOAD_FAST 0 (item)
19 CALL_FUNCTION 1
22 STORE_FAST 1 (x)
4 25 LOAD_FAST 1 (x)
28 LOAD_CONST 1 (100)
31 COMPARE_OP 0 (<)
34 POP_JUMP_IF_FALSE 40
5 37 JUMP_ABSOLUTE 7
6 >> 40 LOAD_CONST 1 (100)
43 LOAD_FAST 1 (x)
46 DUP_TOP
47 ROT_THREE
48 COMPARE_OP 0 (<)
51 JUMP_IF_FALSE_OR_POP 63
54 LOAD_CONST 2 (200)
57 COMPARE_OP 0 (<)
60 JUMP_FORWARD 2 (to 65)
>> 63 ROT_TWO
64 POP_TOP
>> 65 POP_JUMP_IF_FALSE 71
7 68 JUMP_ABSOLUTE 7
8 >> 71 LOAD_CONST 3 (300)
74 LOAD_FAST 1 (x)
77 DUP_TOP
78 ROT_THREE
79 COMPARE_OP 0 (<)
82 JUMP_IF_FALSE_OR_POP 94
85 LOAD_CONST 4 (400)
88 COMPARE_OP 0 (<)
91 JUMP_FORWARD 2 (to 96)
>> 94 ROT_TWO
95 POP_TOP
>> 96 POP_JUMP_IF_FALSE 7
9 99 JUMP_ABSOLUTE 7
102 JUMP_ABSOLUTE 7
>> 105 POP_BLOCK
>> 106 LOAD_CONST 0 (None)
109 RETURN_VALUE
Python不會像大多數其他語言一樣自動優化事物(除非您使用的是PyPy),因此第二個版本可能更快。 但是,除非item
具有需要一段時間的自定義len
實現,否則它也可能不會加快速度。 這種微優化應在分析表明存在問題后保留給緊密的內部循環。
您可以使用dis.dis
檢查此類內容:
import dis
def somefunction1(item):
if len(item) > 10:
print 1
elif len(item) > 10:
print 2
def somefunction2(item):
x = len(item)
if x > 10:
print 1
elif x > 10:
print 2
print "#1"
dis.dis(somefunction1)
print "#2"
dis.dis(somefunction2)
解釋輸出:
#1
4 0 LOAD_GLOBAL 0 (len)
3 LOAD_FAST 0 (item)
6 CALL_FUNCTION 1
9 LOAD_CONST 1 (10)
12 COMPARE_OP 4 (>)
15 POP_JUMP_IF_FALSE 26
[...]
6 >> 26 LOAD_GLOBAL 0 (len)
29 LOAD_FAST 0 (item)
32 CALL_FUNCTION 1
35 LOAD_CONST 1 (10)
38 COMPARE_OP 4 (>)
41 POP_JUMP_IF_FALSE 52
[...]
#2
10 0 LOAD_GLOBAL 0 (len)
3 LOAD_FAST 0 (item)
6 CALL_FUNCTION 1
9 STORE_FAST 1 (x)
11 12 LOAD_FAST 1 (x)
15 LOAD_CONST 1 (10)
18 COMPARE_OP 4 (>)
21 POP_JUMP_IF_FALSE 32
[...]
13 >> 32 LOAD_FAST 1 (x)
35 LOAD_CONST 1 (10)
38 COMPARE_OP 4 (>)
41 POP_JUMP_IF_FALSE 52
您可以看到在第一個示例中, len(item)
被調用了兩次(請參閱兩個CALL_FUNCTION
語句?),而在第二個實現中僅被調用了一個。
這意味着您剩下的問題歸結為如何實現len()
-例如列表,它是O(1)(即便宜),但是對於您可能自己構建的列表,尤其是它不必。
Python沒有將兩者等效。 原因是兩者對於任意函數而言並不等效。 讓我們考慮一下這個函數x()
:
y = 1
def x():
return 1
而這兩個測試:
>>> print(x() + y)
2
>>> print(x() + y)
2
和:
>>> hw = x()
>>> print(hw + y)
2
>>> print(hw + y)
2
這些完全相同,但是,如果我們的功能有副作用怎么辦?
y = 1
def x():
global y
y += 1
return 1
第一種情況:
>>> print(x() + y)
3
>>> print(x() + y)
4
第二種情況:
>>> hw = x()
>>> print(hw + y)
3
>>> print(hw + y)
3
您可以看到該優化僅在函數沒有副作用的情況下有效,否則它將更改程序。 由於Python無法判斷函數是否具有副作用,因此無法進行此優化。
因此,有意義的是在本地存儲該值並重復使用它,而不是一次又一次地調用該函數,盡管現實情況是不太可能發生問題,因為差異很小。 也就是說,它也更具可讀性,意味着您不必重復很多次,因此以這種方式進行編碼通常是一個好主意。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.