Python while 1 和 while True 速度比較


 

References

http://legacy.python.org/dev/peps/pep-0285/
http://stackoverflow.com/questions/3815359/while-1-vs-for-whiletrue-why-is-there-a-difference

本文內容遵從CC3.0版權協議,轉載請注明:轉自Pythoner

本文鏈接地址:Python天坑系列(一):while 1比while True更快?

 

 

1. 前提

  1.1 bool是int的子類

根據PEP285中Review部分第6條所述,bool類是從int類繼承而來的,這樣可以極大的簡化實現(C代碼中調用PyInt_Check()的地方仍將繼續工作)。

  1.2 Python2中True/False不是關鍵字,但Python3中是

 

我們可以導入keyword模塊,來查看關鍵字:

>>> import keyword
>>> keyword.kwlist
['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield']

而在Python3中,關鍵字中添加了True/False/None。

由於Python2中True/False不是關鍵字,因此我們可以對其進行任意的賦值:

>>> (1==1) == True
True
>>> True = "abc"
>>> (1==1) == True
False

 

 

2. True + True = 2

由於bool是繼承自int的子類,因此為了保證向下兼容性,在進行算術運算中,True/False會被當作int值來執行。

 

>>> True + True
2
>>> True - True
0
>>> True * True
1
>>> (True + True) > 1
True
>>> True + 5
6
>>> False + 1
1
>>> 1 / False
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

 

3. While 1比While True快?

首先來看一個比較while 1和while True循環的腳本,兩個函數中,除了1和True的區別之外,其他地方完全相同。

import timeit
def while_one():
    i = 0 
    while 1:
        i += 1
        if i == 10000000:
            break

def while_true():
    i = 0 
    while True:
        i += 1
        if i == 10000000:
            break

if __name__ == '__main__':
    wone = timeit.timeit(while_one,"from __main__ import while_one",number=3)
    wt = timeit.timeit(while_true,"from __main__ import while_true",number=3)
    print "while_one: %s\nwhile_true: %s" % (wone,wt)

 

執行結果:

while_one: 0.937821149826
while_true: 1.39164209366

可以看出wihle 1的執行時間約為while True的2/3   

那么,這是為什么呢?

其實這就是前提中提到的關鍵字的問題。由於Python2中,True/False不是關鍵字,因此我們可以對其進行任意的賦值,這就導致程序在每次循環時都需要對True/False的值進行檢查;而對於1,則被程序進行了優化,而后不會再進行檢查。

 

我們可以通過dis模塊來查看while_one和while_true的字節碼,下面的程序是對剛才的程序進行了一定的簡化后的版本

import dis
def while_one():
    while 1:
        pass

def while_true():
    while True:
        pass
if __name__ == "__main__":
    print "while_one\n"
    dis.dis(while_one)

    print "while_true\n"
    dis.dis(while_true)

 

執行的結果是:

 

while_one

  6           0 SETUP_LOOP               3 (to 6)

  7     >>    3 JUMP_ABSOLUTE            3
        >>    6 LOAD_CONST               0 (None)
              9 RETURN_VALUE        
while_true

 10           0 SETUP_LOOP              10 (to 13)
        >>    3 LOAD_GLOBAL              0 (True)
              6 POP_JUMP_IF_FALSE       12

 11           9 JUMP_ABSOLUTE            3
        >>   12 POP_BLOCK           
        >>   13 LOAD_CONST               0 (None)
             16 RETURN_VALUE  

 

可以看出,正如上面所講到的,在while True的時候,字節碼中多出了幾行語句,正是這幾行語句進行了True值的檢查。

而在Python3中,由於True/False已經是關鍵字了,不允許進行重新賦值,因此,其執行結果與while 1不再有區別(好吧,我這沒有Python3的環境,就不去驗證了,網上有人驗證過了)。但是由於Python2的使用十分廣泛,因此大家不得不注意這個可能會降低性能的地方。

 

4. if x == True: 還是 if x:

在PEP285中,還提到了這兩種寫法的比較。PEP285中認為,==具有傳遞性,a==b, b==c會被化簡為a==c。也就是說,如果選擇前一種寫法的話,6和7在if語句中都應該被認為是真值,那么就會造成6==True==7,被化簡為6==7的問題,因此后一種寫法才是正確的。

現在,讓我們偏個題,假設x就是True,那么程序的執行效率又如何呢?

import timeit

def if_x_equal_true():
    x = True
    if x == True:
        pass

def if_x():
    x = True
    if x:
        pass

if __name__ == "__main__":
    if1 = timeit.timeit(if_x_equal_true,"from __main__ import if_x_equal_true",number = 1000000)
    if2 = timeit.timeit(if_x,"from __main__ import if_x",number = 1000000)

    print "if_x_equal_true: %s\nif_x: %s" % (if1,if2)

執行結果:

if_x_equal_true: 0.127066850662
if_x: 0.0872008800507

 

讓我們再來看看字節碼(程序未作修改,dis的使用方式同上,因此不再給出程序):

 

if_x_equal_true

  7           0 LOAD_GLOBAL              0 (True)
              3 STORE_FAST               0 (x)

  8           6 SETUP_LOOP              16 (to 25)
        >>    9 LOAD_FAST                0 (x)
             12 LOAD_GLOBAL              0 (True)
             15 COMPARE_OP               2 (==)
             18 POP_JUMP_IF_FALSE       24

  9          21 JUMP_ABSOLUTE            9
        >>   24 POP_BLOCK           
        >>   25 LOAD_CONST               0 (None)
             28 RETURN_VALUE        
if_x

 12           0 LOAD_GLOBAL              0 (True)
              3 STORE_FAST               0 (x)

 13           6 SETUP_LOOP              10 (to 19)
        >>    9 LOAD_FAST                0 (x)
             12 POP_JUMP_IF_FALSE       18

 14          15 JUMP_ABSOLUTE            9
        >>   18 POP_BLOCK           
        >>   19 LOAD_CONST               0 (None)
             22 RETURN_VALUE        

 

  

 

可以清晰的看到第9行比第14行,多出了檢查True值和進行比較的操作。

也就是說,不論從遵循PEP的規范,還是執行效率,或者程序的簡潔性來說,我們都應該使用if x:,而不是if x == True:來進行比較。同理,那些if x is not None:之類的語句也應當被簡化為if x:(如果要比較的是非值,而不必須是None的話)。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM