如何在Python中编写类似C语言的do-while(0)循环?

8
在C语言中有一种巧妙的技巧可以避免金字塔式代码,方法是将以下代码转换为:
if (check1())
  if (check2())
    if (check3())
      do_something();

into:

do {
  if (!check1())
    break;

  if (!check2())
    break;

  if (!check3())
    break;

  do_something();
} while (0);

在Python中,最干净的方法是什么?因为Python没有do-while结构。请注意:我并不一定要求在Python中实现do-while循环,而是想要避免上述金字塔式的代码。更新:似乎有些混淆。我使用循环的唯一原因是能够在任何时候中断循环体,这个循环体只会执行一次。本质上,在Python中我正在做的是这样的事情:
while True:
    if not check1():
        break

    if not check2():
        break

    if not check3():
        break

    do_domething()
    break

我在想是否有更简洁的方法。

while (0) <- 永远不会进入这个循环 - inspectorG4dget
3
@inspectorG4dget 的说法不正确,条件是在第一次迭代之后检查的。 - Drew McGowen
2
@inspectorG4dget 这就是重点。 - user2058002
1
那就是重点。(编辑:似曾相识) - Drew McGowen
1
这当然是示例代码的诅咒,但您只会在需要进行更多处理而不仅仅是简单函数调用时才实现do/while技巧。 对于您的示例,即使在C中,if(check1() && check2() && check3()) do-something(); 将是更好的解决方案。 一旦您进入更复杂的嵌套解决方案,实际上使用do/while时,您的Python实现也是不错的。 - tdelaney
显示剩余2条评论
8个回答

7
这个内容的Pythonic写法应该是:
if check1() and check2() and check3():
    do_something()

在Python中,我们强调代码的清晰和简洁,不使用聪明的编程技巧。
[编辑] 如果你需要“创建一个变量并在第一次检查中使用它”,那么你可以使用金字塔式:
if check1():
    #variables and stuff here
    if check2():
        #variables and stuff here
        if check3():
            doSomething()

或者,如@Blender所建议的那样,将其重构为一个单独的方法。这些都比使用不打算循环的循环更简单、更清晰地传达您的意图。

@Brandon:那么你可以使用你的“金字塔式代码”来编写它。虽然可能需要更多的缩进,但这仍然是编写它最简单和最清晰的方式。 - BlueRaja - Danny Pflughoeft
1
在Python中,我们强调代码的清晰和简洁,而不使用巧妙的编程技巧。虽然这是一种美好的情感,但它并不是一个逻辑论证。清晰和简单是主观的概念,随着时间的推移可能会发生变化。仅仅因为有人(甚至很多人)说某种编程方式是清晰和简单的,并不能使其对每个人普遍适用。 - jxh
2
@jxh:如果你从不打算维护你的代码,那么随便怎么做都可以。当Brandon发布原始的C代码时,几乎每个人都误解了它。这不是好代码的标志,无论你使用的编程语言是什么。 - Blender
@Blender:我从不建议写糟糕的代码。但是,任何习语对于使用它的人来说都可以被认为是清晰简单的。这并不意味着其他人也会这样认为。 - jxh
1
@jhx:编写符合惯用法的代码就是要使用在你所使用的语言中被认为“清晰简单”的惯用语(也就是说,对于这种语言的大多数用户而言)。就我而言,我不会将原始的C片段标记为“惯用的C”。 - bruno desthuilliers
显示剩余2条评论

5

反转你的条件并尽早退出。如果你的代码结构良好,你就不必担心首先出现 if 阶梯:

def do_bigger_something():
    if not check1():
        return

    if not check2():
        return

    if not check3():
        return

    do_something()

如果你的代码中有一部分需要进行大量的这些检查,那么很可能应该将其转换为一个函数。


+1 说实话,这是最好的解决方案(在 Python 和 C 中都是)。 - BlueRaja - Danny Pflughoeft
当check1(),check2()和check3()都接受只有调用者可以访问的多个参数时,这不是一个好的解决方案。然后调用者必须提供所有参数给do_bigger_something()函数使用。 - some user

2
if (check1() and check2() and check3()):
    do_something()

1

如果检查不像这样简单(可以使用简单的and完成),我建议重构代码并将其拆分为一个函数,而不是滥用循环来做它本不应该做的事情。

def doSomethingIfConditions():

  if not check1():
    return

  if not check2():
    return

  if not check3():
    return

  doSomething()

...your code...
doSomethingOnConditions()
...your code...

1
  if check1() and  check2() and  check3():  
         do_something()

2
那不会成为一个无限循环吗? - user2058002
我意识到当我打字时...这就是为什么它现在消失了... :) - Jon Kiparsky

0

解决方案1(直接):

while True:
    if not check1():
        break
    if not check2():
        break
    if not check3():
        break
    do_something()
    break

解决方案 #2(更具 Python 风格):

for check in check1, check2, check3:
    if not check():
        break
else:
    # the else part is only executed
    # if we didn't break out the loop
    do_something()

解决方案 #3(更加Pythonic):

if all(c() for c in check1,check2, check3):
    # all(seq) will stop at the first false result
    do_something()

有缩进问题吗,或者在for循环中可以使用else(而不是if)吗? - Jonathan Leffler
@JonathanLeffler:没有缩进问题,for语句有一个可选的else块,只有在您没有跳出循环时才会执行。这在这里有记录:http://docs.python.org/2/reference/compound_stmts.html#the-for-statement - bruno desthuilliers

0

我不认为需要循环...你最终还是会跳出它。

if all((check1(), check2(), check3())):
    print "Do something"

如果需要,也可以选择块级样式。


-2
在C语言中,使用您展示的do ... while(0)结构通常用于当C程序员想要像使用实际goto一样行事时,但由于各种原因无法使用实际goto。因此,从do ... while(0)break出来实际上是一种hack。在Python中使用相同的习惯用法将会延续这种hack。
在C语言中,我通常避免使用do ... while(0)的这种特定用法,而选择检查函数。在Python中,这将是:
def do_checks():
    if not check1():
        return False
    if not check2():
        return False
    if not check3():
        return False
    return True

if do_checks():
    do_something()

C语言中do ... while(0)结构的最直接翻译可能是只有一次迭代的循环。 不要这样做。

for x in range(1):
    if not check1():
        break
    if not check2():
        break
    if not check3():
        break
    do_something()

我认为这个被接受的方法最接近我提到的 do { ... } while(0) 技巧。 - user2058002
1
你不是在写Python,而是在写Python中的C代码。 - BlueRaja - Danny Pflughoeft
1
是的,这个想法很糟糕。 - kindall
由于作者更新了他的答案并强调原问题的答案不应该使用,我重新接受了他的回答,因为它最接近我所要问的内容。 - user2058002

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