为什么在Python中`finally`语句块中不允许使用`continue`?

43
以下代码引发语法错误:
>>> for i in range(10):
...     print i
...     try:
...        pass
...     finally:
...        continue
...     print i
...
  File "<stdin>", line 6
SyntaxError: 'continue' not supported inside 'finally' clause

为什么finally子句中不允许使用continue语句?

P.S. 另一段代码则没有问题:

>>> for i in range(10):
...     print i
...     try:
...        pass
...     finally:
...        break
...
0

如果有影响的话,我正在使用 Python 2.6.6。


3
看起来只是纯粹的懒惰?http://www.gossamer-threads.com/lists/python/dev/484210 - Mike Christensen
@Mike Christensen:我也找到了那个线程,但文档说:“continue 只能在语法上嵌套在 for 或 while 循环中,而不能嵌套在函数或类定义中,或者在该循环的 finally 子句中”。所以这是懒惰还是后来需要更改的事情呢?就像 Python 中的许多事情一样... - ElenaT
你读完整个线程了吗?底部有一些有趣的信息,关于在finally块中使用continue语句实际意义以及可能出现的各种问题。值得一读。 - Mike Christensen
@Mike Christensen:糟糕...漏掉了。确实,对于描述的这种情况,这是有道理的。 - ElenaT
7个回答

26

在 finally 块中使用 continue 是被禁止的,因为它的解释会带来问题。如果 finally 块由于异常而被执行,你会怎么做?

for i in range(10):
    print i
    try:
       raise RuntimeError
    finally:
       continue        # if the loop continues, what would happen to the exception?
    print i

在处理这段代码时,我们可以决定它应该做什么,比如吞掉异常;但良好的语言设计建议相反。如果代码让读者感到困惑,或者有更清晰的表达意图的方式(可能是通过 try: ... except Exception: pass; continue),那么将其保留为SyntaxError会有一些优势。

有趣的是,你可以在finally块中放置return,它将吞掉所有异常,包括KeyboardInterruptSystemExitMemoryError。这可能也不是个好主意 ;-)


4
我同意。通常情况下,使用finally从句是为了处理异常打断程序时存在的未完成事项。而“最终继续”(在英语中也没有意义)可能会导致重复异常直到循环结束,甚至在影响循环条件本身的问题时可能会无限重复它。 - calebds
3
在 finally 块中使用 continue 可以与 return 或 raise 处理方式相同。在这个例子中,异常将被忽略,循环将继续执行。 - Peter Graham
3
我认为人们忘记了finally代码块中的代码始终会被执行。 - jathanism

6
Python语言参考手册禁止在finally子句内使用continue我不是很确定为什么。也许是因为try子句中的continue确保了finally的执行,而在finally子句中决定continue应该做什么有些模糊。
编辑:@Mike Christensen对问题的评论指出了一个线程,在这个线程中Python核心开发人员讨论了这种结构的歧义性。此外,在超过九年的Python使用中,我从未想过要这样做,所以这可能是开发人员不愿花费太多时间解决的相对较少见的情况。

对我来说,这似乎是一个很好的解释。finallycontinue的组合肯定值得重构。 - jsalonen
2
是的,在这些情况下,有时最好不要让开发人员做某些事情 - 否则您必须规定、解决和修复由允许此类事情引起的任何奇怪问题。 - Mike Christensen

6

2

我认为这个原因其实非常简单。在finally关键字后面的continue语句每次都会被执行,这是finally语句的特性。无论你的代码是否抛出异常都无关紧要,finally一定会被执行。

因此,你的代码...

for i in range(10):
   print i
   try:
       pass
   finally:
       continue
   print i # this (and anything else below the continue) won't ever be executed!

等价于以下代码...

for i in range(10:
    print i
    try:
        pass
    finally:
        pass

Python更加简洁清晰。因为continue语句后面的代码永远不会被执行,所以Python不允许在finally块中使用continue语句。(稀疏比密集好。)


“finally”关键字后的“continue”语句每次都会执行:除非它不是唯一的语句,例如如果是“if something: continue”。而且Python通常不会因为在“continue”之后有无法到达的代码而产生SyntaxError,所以这并不是一个有效的理由。 - Artyer

2

我没有在其他回答中看到这个提到,但我认为在这种情况下你可能需要的是try..else

for i in range(10):
    print i
    try:
       #pass <= I commented this out!
       do_something_that_might_fail(i)
    except SomeException:
       pass
    else:
       continue
    print i
else块仅在没有异常时执行。这意味着:
  1. 我们print i
  2. 我们尝试执行可能失败的do_something_that_might_fail(i)
  3. 如果它抛出SomeException,则继续执行并再次print i
  4. 否则,我们continue(并且i不会被打印)

2

使用 continue 可能会导致异常被抛出但被忽略,这是一个强有力的论点,但当你使用 breakreturn 时,异常也会被忽略。

例如,下面的代码同样会忽略异常:

for i in range(10):
    print i
    try:
        raise Exception
    finally:
        break
    print i       # not gonna happen

当在一个函数中时,这个方法可以正常工作且异常被吞掉了:

for i in range(10):
    print i
    try:
        raise Exception
    finally:
        return
    print i       # not gonna happen

那么为什么在finally块中允许使用breakreturn,无论是否有可能引发错误,但不允许使用continue呢?
您还可以考虑以下因素:
  • finally始终被执行;
  • continue“中止”当前迭代。
这意味着,在每个循环内部,由于finally始终执行,您将始终拥有一个continue,它基本上是说“中止当前迭代”,“中止当前迭代”,“中止当前迭代”...这没有任何意义。但是使用breakreturn也没有意义。当前迭代也会被中止,唯一的区别是现在只剩下一个迭代。
因此,“为什么不允许在finally中使用continue?”这个问题也可以问为“为什么允许breakreturn?”。
也许因为当时不允许这样做是有道理的?这是开发人员的决定,现在就像它一样?当然,这也可能是实现者的懒惰,但谁知道,也许他们有什么想法,在Python的另一个版本中,以另一种方式实现会更有意义?
思路是,这里的示例只是极端情况。您不会只是编写那样的代码,对吧?在finally块中肯定有一些逻辑来确定何时使用break/return/continue,而不是像那样直截了当。因此,在我看来,在finally中使用continue应该是允许的,因为如果需要,我会喜欢编写干净的代码,并在finally中使用continue,而不是为了解决此限制而采用代码解决方法(即在Python的哲学中,“我们都是成年人”)。

1
我有点晚参与这个讨论,但是在finally子句中使用continue语句并不一定意味着每次迭代都会被中止,因为你可以将continue语句包装在一个条件语句中(这仍然是一个语法错误)。 - ashastral

-1

现在这个功能已经在3.8版本中发布了。

https://docs.python.org/3/whatsnew/3.8.html

示例代码

def new_f():
    for i in range(0,24):
        try:
            print(1/0)
        except:
            print('In Exception')
        finally:
            print('In finally')
            continue

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