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)
复制代码

 

执行的结果是:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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的使用方式同上,因此不再给出程序):

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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的话)。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值