"while 1"和"while True"有什么区别?(这是一个关于IT技术的提问)

28
我见过两种创建Python无限循环的方法:
  1. while 1:
        do_something()
    
  2. while True:
        do_something()
    
    这两者有区别吗?哪一个更符合Pythonic的风格?


7
无限循环可能不符合Python的惯用方式?(参见balpha) - Blauohr
11个回答

48

基本上并不重要,这些细节并不会影响某个东西是否“Pythonic”。

但是,如果您对琐事感兴趣,那么有一些区别。

  1. 内置布尔类型直到 Python 2.3 才存在,因此旨在运行在古老版本的代码往往使用 while 1:形式。例如,在标准库中就可以看到这种形式的代码。

  2. 在 Python 3 之前,True 和 False 这两个关键字 不是保留字,因此可以被赋值,改变它们的值。这有助于上面的情况,因为代码可以使用 True = 1 实现向后兼容性,但意味着每次使用名称 True 都需要在全局字典中查找。

  3. 由于上述限制,两个版本编译的字节码在 Python 2 中是不同的,因为对于 True 它无法使用常量整数的优化技术。因为 Python 可以在编译 1 时确定它总是非零的,所以它删除了条件跳转,并根本不加载常量:

  4. >>> import dis
    >>> def while_1():
    ...     while 1:
    ...         pass
    ...
    >>> def while_true():
    ...     while True:
    ...         pass
    ...
    >>> dis.dis(while_1)
      2           0 SETUP_LOOP               5 (to 8)
    
      3     >>    3 JUMP_ABSOLUTE            3
                  6 POP_TOP
                  7 POP_BLOCK
            >>    8 LOAD_CONST               0 (None)
                 11 RETURN_VALUE
    >>> dis.dis(while_true)
      2           0 SETUP_LOOP              12 (to 15)
            >>    3 LOAD_GLOBAL              0 (True)
                  6 JUMP_IF_FALSE            4 (to 13)
                  9 POP_TOP
    
      3          10 JUMP_ABSOLUTE            3
            >>   13 POP_TOP
                 14 POP_BLOCK
            >>   15 LOAD_CONST               0 (None)
                 18 RETURN_VALUE
    
    所以,while True: 更易于阅读,while 1: 对旧版本的Python更加友好。由于现在不太可能需要在Python 2.2上运行或担心循环的字节码计数,前者略微更优。

2
我想指出的是,在我谦虚的意见中,最符合Python风格的做法是不要为这样的细节烦恼。虽然这篇文章非常有趣和有启发性,我很欣赏,但我仍然坚持认为“while True:”更符合Python风格。至少在我解释奇怪术语“Pythonic”的方式中是这样,你的看法可能会有所不同。 - porgarmingduod
如果你真的很努力,你仍然可以在3.x中重新定义True。在3.0中,import builtins; builtins.__dict__['True'] = 0就可以了;在3.3中,你需要更有创意,但仍然是可能的。如果情况变得最糟,你总可以通过ctypes进入C API(或其他实现的等效方式)。有趣的是编译器“假定”你不能这样做,因此某些表达式将被编译掉,而其他表达式将以一种依赖于实现/版本的方式使用你的新值。如果你能找到一个好用途……你可能会找到更好的利用时间的方法…… - abarnert

12
最符合Python风格的代码也将是最易读懂的。使用 while True:

1
"while 1:" 和 "for(;;)" 是非常不同的 :-) - sharjeel
8
@nomemory:这个问题是关于“Pythonic”的,与C程序员几乎没有任何关系。如果你想要为C程序员提供可读性,请使用C语言。 - S.Lott
3
@S.Lott:我想说的是,可读性是一种非常相对的东西。 - Andrei Ciobanu

5

这并不重要。两者都很容易阅读和理解,不过个人偏向于始终使用while True,因为它更明确。

更一般地说,在Python中,很多while-break循环实际上可以用其他方式实现。有时我看到人们写成i = 0; while True: i += 1 ...,这可以替换为for i in itertools.count(),还有人写while True: foo = fun() if foo is None: break,但实际上可以写成for foo in iter(fun, None),虽然需要学习,但可以减少冗长的代码和出错的机会。


4

两者皆不是。

两种方法都需要我扫描代码来查找break,而无法在代码所在的位置看到停止条件。

我尽可能避免这种情况的发生,如果无法避免,则让代码自己说明:

while not found_answer:
    check_number += 1
    if check_number == 42:
        found_answer = True

编辑: 看起来上面提到的“避免”这个词并不够清晰。使用基本上是无限循环并在循环内的某个地方离开它(使用break通常应该完全避免。有时候那是不可能的。在这种情况下,我喜欢使用像上面的代码一样的东西,然而,它仍然代表着相同的概念 - 上面的代码只不过是一个妥协 - 但至少,我可以在开始时就展示循环的目的 – 就像我不会把一个函数称为do_something_with_args(*args)


7
在你的代码中,你只需要扫描 found_answer = True 这行代码就行了。 - Mike Graham
7
我不认为这是更好的选择。使用“break”的原因通常是停止条件无法轻松或高效地表达。如果你真正意思是使用像“found_answer”这样的变量,那么你就必须扫描而不是使用“break” - 并且通常仍然需要使用“continue”而不是“break”来使其退出循环。 - Scott Griffiths
@Mike Graham:是的,我说过我尽量避免使用它。如果我使用它,我会尝试通过为布尔变量命名以解释使循环停止的原因来使事情清晰(即可读)。 - balpha
1
@Scott Griffiths:但与True不同,not found_answernot received_quit_command向代码读者传达了一些信息。 - balpha
3
这是一个公正的观点,但我感觉你引入了一个新变量并增加了一个额外的条件评估,只是为了让循环的意图更清晰,而实际上你只需要在开头加上一个简单的注释就可以了。 - Scott Griffiths
@Scott Griffiths: ...以及break语句(们)。但是是的,这可能只是个人喜好的问题;我喜欢在可以的情况下使用函数和变量名作为“注释”;但是你的观点也是正确的。我的观点仍然是不管怎样,尝试做一些完全不同的事情。我真的无法理解为什么我的回答没有清楚地传达这一点。 - balpha

3

在我看来,第二个选项更加明显。

如果你能够摆脱 while 循环并写出更加紧凑的代码,那可能会更符合 Python 的风格。
例如:

# Get the even numbers in the range 1..10
# Version 1
l = []
n = 1
while 1:
    if n % 2 == 0: l.append(n)
    n += 1
    if n > 10: break
print l

# Version 2
print [i for i in range(1, 11) if i % 2 == 0]

# Version 3
print range(2, 11, 2)

2

我认为这主要是一个风格问题。两种方式都应该能够轻松理解为无限循环。

然而,个人更喜欢第二个选项。这是因为它只需要少量的心理步骤来理解,特别是对于没有C语言背景的程序员。


2
第一个方法也适用于那些还未定义True的早期版本。

1
在这个时候,一旦你开始为Python 3编程,保持向后兼容性基本上是毫无意义的。 - Xorlev
我想知道Python的最后一个版本是哪个不支持True? - Eli Bendersky

2
如果你有一个应该在有限时间内终止的算法,我建议使用以下代码,这比使用“while True”更安全:
maxiter = 1000
for i in xrange(maxiter):
   # your code
   # on success:
     break
else:
   # that algorithm has not finished in maxiter steps! do something accordingly

在这方面,我持有不同意见,而且有一个著名的学派认为应该尽可能地明确标准。如果你期望一个值为零,请明确写出 "== 0",而不要依赖于 "<= 1",因为-1可能表示前面处理的完全意外的分支;无限循环肯定会被某个人检测到,而else后面的代码仍然必须确保这一点。 - guidot

1

我认为第二个表达更加明确,因此更符合Pythonic的风格。


0
更好的方式是使用“while True”循环,并带有条件跳出循环。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接