Python天坑系列(一):while 1比while True更快?
0. 前言
前些天被Python的多线程坑了一把,因此产生了写一个《Python天坑系列》博客的想法,说说我碰到的那些Python的坑。
而天坑这个词呢,一方面指Python的坑,另一方面也说明本系列文章也是个坑,对于会写什么内容、有多少篇、多久更新一次、什么时间更新我都无法确定,哈哈(看,之前已经3个月没有更新过了!)。
本篇是系列的第一篇,讲的内容是Python的bool类型。
1. 前提
1.1 bool是int的子类
根据PEP285中Review部分第6条所述,bool类是从int类继承而来的,这样可以极大的简化实现(C代码中调用PyInt_Check()的地方仍将继续工作)。
1.2 Python2中True/False不是关键字,但Python3中是
我们可以导入keyword模块,来查看关键字:
| 
					 1 2 3 4 5  | 
						# Python2 关键字 >>> 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 2 3 4 5  | 
						>>> (1 == 1) == True True >>> True = "pythoner.com" >>> (1 == 1) == True      False  | 
					
2. True + True = 2
由于bool是继承自int的子类,因此为了保证向下兼容性,在进行算术运算中,True/False会被当作int值来执行。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14  | 
						>>> True + True 2 >>> True - True 0 >>> True * True 1 >>> (True + True) > 1 True >>> True + 5 6 >>> 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的区别之外,其他地方完全相同。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23  | 
						#! /usr/bin/python # -*- coding: utf-8 -*- 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__":     w1 = 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" % (w1, wt)  | 
					
执行结果:
while one: 1.37000703812
while_true: 2.07638716698
可以看出wihle 1的执行时间约为while True的2/3。
那么,这是为什么呢?
其实这就是前提中提到的关键字的问题。由于Python2中,True/False不是关键字,因此我们可以对其进行任意的赋值,这就导致程序在每次循环时都需要对True/False的值进行检查;而对于1,则被程序进行了优化,而后不会再进行检查。
我们可以通过dis模块来查看while_one和while_true的字节码,下面的程序是对刚才的程序进行了一定的简化后的版本。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19  | 
						#! /usr/bin/python # -*- coding: utf-8 -*-    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,那么程序的执行效率又如何呢?
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  | 
						#! /usr/bin/python                                                                           # -*- coding: utf-8 -*- import timeit def if_x_eq_true():     x = True     if x == True:         pass def if_x():     x = True     if x:         pass if __name__ == "__main__":     if1 = timeit.timeit(if_x_eq_true, "from __main__ import if_x_eq_true", number = 1000000)     if2 = timeit.timeit(if_x, "from __main__ import if_x", number = 1000000)     print "if_x_eq_true: %s\nif_x: %s" % (if1, if2)  | 
					
执行结果是:
if_x_eq_true: 0.212558031082
if_x: 0.144327878952
让我们再来看看字节码(程序未作修改,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  | 
						if_x_eq_true   8           0 LOAD_GLOBAL              0 (True)               3 STORE_FAST               0 (x)   9           6 LOAD_FAST                0 (x)               9 LOAD_GLOBAL              0 (True)              12 COMPARE_OP               2 (==)              15 POP_JUMP_IF_FALSE       21  10          18 JUMP_FORWARD             0 (to 21)         >>   21 LOAD_CONST               0 (None)              24 RETURN_VALUE         if_x  13           0 LOAD_GLOBAL              0 (True)               3 STORE_FAST               0 (x)  14           6 LOAD_FAST                0 (x)               9 POP_JUMP_IF_FALSE       15  15          12 JUMP_FORWARD             0 (to 15)         >>   15 LOAD_CONST               0 (None)              18 RETURN_VALUE          | 
					
可以清晰的看到第9行比第14行,多出了检查True值和进行比较的操作。
也就是说,不论从遵循PEP的规范,还是执行效率,或者程序的简洁性来说,我们都应该使用if x:,而不是if x == True:来进行比较。同理,那些if x is not None:之类的语句也应当被简化为if x:(如果要比较的是非值,而不必须是None的话)。
5. References
http://legacy.python.org/dev/peps/pep-0285/
http://stackoverflow.com/questions/3815359/while-1-vs-for-whiletrue-why-is-there-a-difference

不错
python 3.4 true vs 1:![[em_xiao]](http://www.pythoner.com/wp-includes/images/smilies/xiao.gif)
while one: 3.1496432364352462
while_true: 3.502516701034144
如果您还有闲功夫,可以试试用Python2执行是一下速度。我的执行完Python3没错是时间基本相似,然而比Python2的while 1 慢,比while True快。….基本Python3的计算速度等于Python2的两个速度相加求平均。什么鬼
感谢分享,学习了
请问这个系列还有更新吗?还是已经弃坑到别的语言了![[em_yali]](http://www.pythoner.com/wp-includes/images/smilies/yali.gif)
之后就没有更新过了,时间上不允许,所以也很少写博客了
while one: 2.3498612695192227
while_true: 2.0453079039757096
python 3.6.3
while_one: 2.7856697027955097
while_true: 2.6668411494566975
while one: 1.2918369409179604
while_true: 1.2617157613620975
python 3.6.4
aaa
你测试的是python2,py2和py3没有可比性的,因为py3的整体速度都是快过py2的。True是py3新添加的关键字,如果True的速度还不如1的话,那么py的维护团队真的是搞笑来的了。
您好我不太理解6==True==7传递性那段,如果==可传递的话,为什么我用if 6==7 跑出来显示会是False呢?