Python:如何让for循环从函数继续?

28 投票
9 回答
27961 浏览
提问于 2025-04-16 18:01

有时候我在一个 for 循环里需要用到下面这种模式。有时候在同一个循环里还需要用好几次:

try:
    # attempt to do something that may diversely fail
except Exception as e:
    logging.error(e)
    continue

现在我觉得没有一个好的办法把这个包裹成一个函数,因为它不能 return continue

def attempt(x):
    try:
        raise random.choice((ValueError, IndexError, TypeError))
    except Exception as e:
        logging.error(e)
        # continue  # syntax error: continue not properly in loop
        # return continue  # invalid syntax
        return None  # this sort of works

如果我用 return None,那么我可以:

a = attempt('to do something that may diversely fail')
if not a:
    continue

但是我觉得这样做不太合适。我想在 attempt 函数里告诉这个 for 循环去 continue(或者说假装继续)。

9 个回答

4

除了上下文,我想简单回答这个问题。首先,函数不能让它被调用的循环继续。这是因为函数不知道这个循环的情况。而且,如果函数在没有循环的情况下被调用,那就会引发一系列新的问题,比如这个函数该怎么处理这个continue

但是,函数可以通过不同的方式来“通知”调用它的地方,表示它希望继续当前的循环。其中一种方式就是返回值。例如,返回FalseNone来表示这个意思。另一种通知方式是抛出一个特殊的Exception

class ContinuePlease(Exception): pass

def f():
    raise ContinuePlease()

for i in range(10):
    try:
        f()
    except ContinuePlease:
        continue
8

异常的主要概念是,它们可以在多个层级之间传递。也就是说,如果你在调用的某个深层次的函数里遇到了错误(或者其他特别的情况),你仍然可以在更高的层级捕捉到这个错误,并且妥善处理它。

举个例子,假设你有一个叫做 attempt() 的函数,它会调用 attempt2() 和 attempt3() 这两个函数,而 attempt3() 可能会遇到一个特别的情况,这个情况会导致主循环停止:

class JustContinueException(Exception):
    pass

for i in range(0,99):
    try:
        var = attempt() # calls attempt2() and attempt3() in turn
    except JustContinueException:
        continue # we don't need to log anything here
    except Exception, e:
        log(e)
        continue

    foo(bar)

def attempt3():
    try:
        # do something
    except Exception, e:
        # do something with e, if needed
        raise # reraise exception, so we catch it downstream

你甚至可以自己抛出一个虚假的异常,这样就会导致循环停止,而且这个异常不会被记录下来。

def attempt3():
    raise JustContinueException()
16

Python已经有一种很好的方式来做到这一点,而且不需要用到continue

for i in range(10):
    try:
        r = 1.0 / (i % 2)
    except Exception, e:
        print(e)
    else:
        print(r)

不过我建议不要再嵌套更多层了,否则你的代码会变得很难看。

在你的情况下,我可能会这样做,因为这样更容易对每个函数进行单元测试,而且平坦的结构比嵌套的结构更好

#!/usr/bin/env python

def something_that_may_raise(i):
    return 1.0 / (i % 2)

def handle(e):
    print("Exception: " + str(e))

def do_something_with(result):
    print("No exception: " + str(result))

def wrap_process(i):
    try:
        result = something_that_may_raise(i)
    except ZeroDivisionError, e:
        handle(e)
    except OverflowError, e:
        handle(e) # Realistically, this will be a different handler...
    else:
        do_something_with(result)

for i in range(10):
    wrap_process(i)

记得总是捕获特定的异常。如果你没有预料到会抛出某个特定的异常,那继续处理你的循环可能就不安全了。

编辑以下评论:

如果你真的不想处理异常,虽然我还是觉得这是个坏主意,那就捕获所有异常(except:),而不是用handle(e),直接用pass。这样一来,wrap_process()就会结束,跳过else:块中真正的工作,然后进入你的for循环的下一次迭代。

请记住,错误不应该悄无声息地过去

撰写回答