【问题标题】:while (1) vs. while(True) -- Why is there a difference (in python 2 bytecode)?while (1) vs. while(True) -- 为什么有区别(在 python 2 字节码中)?
【发布时间】:2011-04-18 10:32:19
【问题描述】:

对这个关于 perl 中无限循环的问题感兴趣:while (1) Vs. for (;;) Is there a speed difference?,我决定在 python 中进行类似的比较。我预计编译器会为while(True): pass和while(1): pass生成相同的字节码,但在python2.7中实际上并非如此。

以下脚本:

import dis

def while_one():
    while 1:
        pass

def while_true():
    while True:
        pass

print("while 1")
print("----------------------------")
dis.dis(while_one)

print("while True")
print("----------------------------")
dis.dis(while_true)

产生以下结果:

while 1
----------------------------
  4           0 SETUP_LOOP               3 (to 6)

  5     >>    3 JUMP_ABSOLUTE            3
        >>    6 LOAD_CONST               0 (None)
              9 RETURN_VALUE        
while True
----------------------------
  8           0 SETUP_LOOP              12 (to 15)
        >>    3 LOAD_GLOBAL              0 (True)
              6 JUMP_IF_FALSE            4 (to 13)
              9 POP_TOP             

  9          10 JUMP_ABSOLUTE            3
        >>   13 POP_TOP             
             14 POP_BLOCK           
        >>   15 LOAD_CONST               0 (None)
             18 RETURN_VALUE        

使用while True 明显更复杂。这是为什么呢?

在其他情况下,python 的行为就像 True 等于 1:

>>> True == 1
True

>>> True + True
2

while为什么要区分两者?

我注意到 python3 确实使用相同的操作来评估语句:

while 1
----------------------------
  4           0 SETUP_LOOP               3 (to 6) 

  5     >>    3 JUMP_ABSOLUTE            3 
        >>    6 LOAD_CONST               0 (None) 
              9 RETURN_VALUE         
while True
----------------------------
  8           0 SETUP_LOOP               3 (to 6) 

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

python3 对布尔值的评估方式是否有变化?

【问题讨论】:

标签: python


【解决方案1】:

在 Python 2.x 中,True 不是关键字,而只是在 bool 类型中定义为 1 的 built-in global constant。因此解释器仍然需要加载True 的内容。换句话说,True 是可重新分配的:

Python 2.7 (r27:82508, Jul  3 2010, 21:12:11) 
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> True = 4
>>> True
4

在 Python 3.x 中 it truly becomes a keyword 和一个实常数:

Python 3.1.2 (r312:79147, Jul 19 2010, 21:03:37) 
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> True = 4
  File "<stdin>", line 1
SyntaxError: assignment to keyword

因此解释器可以用无限循环替换while True: 循环。

【讨论】:

【解决方案2】:

不完全正确,称之为“无限循环”

因此解释器可以用无限循环替换 while True: 循环。

因为仍然可以跳出这样的while True: 循环。但是这样的循环的else 子句在 Python 3 中永远不会被访问。

Python 3 对 True 值查找的简化使其运行速度与 Python 2 中的 while 1 一样快。

性能比较

展示一个有点不平凡的while循环的时间差异:

设置

def while1():
    x = 0
    while 1:
        x += 1
        if x == 10:
            break
            
def whileTrue():
    x = 0
    while True:
        x += 1
        if x == 10:
            break

Python 2

>>> import timeit
>>> min(timeit.repeat(while1))
0.49712109565734863
>>> min(timeit.repeat(whileTrue))
0.756627082824707

Python 3

>>> import timeit
>>> min(timeit.repeat(while1))
0.6462970309949014
>>> min(timeit.repeat(whileTrue))
0.6450748789939098

说明

为了解释差异,在 Python 2 中:

>>> import keyword
>>> 'True' in keyword.kwlist
False

但在 Python 3 中:

>>> import keyword
>>> 'True' in keyword.kwlist
True
>>> True = 'true?'
  File "<stdin>", line 1
SyntaxError: can't assign to keyword

由于True 是 Python 3 中的关键字,解释器不必查找该值以查看是否有人将其替换为其他值。但由于在 Python 2 中可以将 True 分配给另一个值,因此解释器每次都必须查找它。

Python 2 的结论

如果您在 Python 2 中有一个紧凑、长时间运行的循环,您可能应该使用 while 1: 而不是 while True:。

Python 3 的结论

如果您没有跳出循环的条件,请使用while True:。

【讨论】:

    【解决方案3】:

    这是一个已有 7 年历史的问题,已经有一个很好的答案,但是问题中的一个误解,在任何答案中都没有解决,这使得它可能会混淆其他一些标记为重复的问题.

    在其他情况下,python 表现得好像 True 等于 1:

    >>> True == 1
    True
    
    >>> True + True
    2
    

    while为什么要区分两者?

    事实上,while 在这里并没有做任何不同的事情。它以与+ 示例完全相同的方式区分1 和True。


    这里是 2.7:

    >>> dis.dis('True == 1')
      1           0 LOAD_GLOBAL              0 (True)
                  3 LOAD_CONST               1 (1)
                  6 COMPARE_OP               2 (==)
                  9 RETURN_VALUE
    
    >>> dis.dis('True == 1')
      1           0 LOAD_GLOBAL              0 (True)
                  3 LOAD_GLOBAL              0 (True)
                  6 BINARY_ADD
                  9 RETURN_VALUE
    

    现在比较:

    >>> dis.dis('1 + 1')
      1           0 LOAD_CONST               1 (2)
                  3 RETURN_VALUE
    

    它为每个True 发出一个LOAD_GLOBAL (True),优化器对全局无能为力。因此,while 区分 1 和 True 的原因与 + 完全相同。 (并且== 不会区分它们,因为优化器不会优化比较。)


    现在比较 3.6:

    >>> dis.dis('True == 1')
      1           0 LOAD_CONST               0 (True)
                  2 LOAD_CONST               1 (1)
                  4 COMPARE_OP               2 (==)
                  6 RETURN_VALUE
    
    >>> dis.dis('True + True')
      1           0 LOAD_CONST               1 (2)
                  2 RETURN_VALUE
    

    在这里,它为关键字发出LOAD_CONST (True),优化器可以利用它。因此,True + 1 不 区分,原因与while True 不区分的原因完全相同。 (并且== 仍然无法区分它们,因为优化器不会优化比较。)


    同时,如果代码没有被优化,解释器最终会在所有这三种情况下处理True 和1 完全相同。 bool 是int 的子类,它的大部分方法都继承自int,而True 的内部整数值为1。所以,无论你是在做while 测试(__bool__在 3.x 中,__nonzero__ 在 2.x 中)、比较 (__eq__) 或算术 (__add__),无论您使用 True 还是 1,都在调用相同的方法。

    【讨论】:

      猜你喜欢
      • 2011-01-16
      • 2023-03-14
      • 2014-05-19
      • 2013-04-10
      • 1970-01-01
      • 2013-03-02
      • 2019-10-07
      • 1970-01-01
      • 2013-10-29
      相关资源
      最近更新 更多