【问题标题】:Python: How to tell the for loop to continue from a function?Python:如何告诉 for 循环从函数继续?
【发布时间】:2011-08-29 14:06:03
【问题描述】:

有时我需要在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(或伪造它)。

【问题讨论】:

  • 为什么会有将四行代码 [try-except-log-continue] 包装成不那么透明的冲动?
  • 在函数内部引发异常然后在循环中捕获它有什么问题?
  • 你做错了。如果您将 for 循环用作常见模式,请将 that 包装在函数中,而不是循环的元素。
  • 几年后看这个,“except Exception:”或“except Exception as e:”是最恶魔的Python反模式realpython.com/blog/python/…

标签: python continue


【解决方案1】:

Python 已经有一个非常好的结构来执行此操作,并且它不使用 continue

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

不过,我不会嵌套更多,否则您的代码很快就会变得非常难看。

在你的情况下,我可能会做更多这样的事情,因为对单个函数和flat is better than nested进行单元测试要容易得多@:

#!/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)

请记住始终catch specific exceptions。如果您不希望抛出 特定 异常,则继续处理循环可能不安全。

编辑以下 cmets:

如果你真的不想处理异常,我仍然认为这是一个坏主意,那么捕获所有异常 (except:) 而不是 handle(e),只需 pass。此时wrap_process() 将结束,跳过真正工作完成的else:-block,您将进入for-loop 的下一次迭代。

记住,Errors should never pass silently

【讨论】:

  • @Frank Malina:很抱歉听到这个消息。哪个部分不符合您的要求? else: 对我来说似乎是解决这种情况的最 Pythonic 方式。
  • 我不想处理异常,我只想指示程序在出现异常时继续最近的封闭循环的下一个循环。
  • @Frank Malina:如果循环中的其他所有内容都在 else:-block 中,(为了整洁,包装在 do_something_with() 中),那么这将跳过循环的其余部分并进入下一个随心所欲地循环。尝试运行我的示例代码以获得更完整的演示。
  • @Frank Malina,看起来 Johnsyweb 的答案完全符合您在问题中的描述。您编写的代码和 Johnsyweb 提供的代码在 Python 中的工作方式相同。您真的应该尝试一下或仔细查看它。
  • @Frank Malina:有时您必须消除特定错误。日志记录不是静默。
【解决方案2】:

异常的整个想法是它们跨越多个间接级别,即,如果您在调用层次结构深处有错误(或任何其他异常状态),您仍然可以捕获它在更高的层次上并妥善处理。

在您的情况下,假设您有一个函数尝试(),它在调用层次结构中调用函数尝试2()和尝试3(),并且尝试3()可能会遇到异常状态,这应该导致主循环终止:

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()

【讨论】:

  • you can still catch it on a higher level and handle it properly --> 非常有用的提醒 :)
【解决方案3】:

也许你想做延续?你可以去看看Eric Lippert explains them 是如何做到的(如果你准备好大吃一惊,但在 Python 中它可能看起来有点像这样:

def attempt(operation, continuation):
    try:
        operation()
    except:
        log('operation failed!')
    continuation()

在你的循环中你可以这样做:

attempt(attempt_something, lambda: foo(bar)) # attempt_something is a function

【讨论】:

    【解决方案4】:

    除了上下文,我只想简短地回答这个问题。不,一个函数不能continue 一个可能被调用的循环。那是因为它没有关于这个上下文的信息。此外,它还会引发一系列全新的问题,例如如果调用该函数而不使用环绕循环来处理 continue 会发生什么?

    但是一个函数可以通过各种方式表示它希望调用者continue它当前执行的任何循环。一种方法当然是返回值。例如,返回 FalseNone 以发出信号。另一种表示这一点的方式是提出一个特殊的Exception

    class ContinuePlease(Exception): pass
    
    def f():
        raise ContinuePlease()
    
    for i in range(10):
        try:
            f()
        except ContinuePlease:
            continue
    

    【讨论】:

      【解决方案5】:

      你可以用这个:

      for l in loop:
        attempt() and foo(bar)
      

      但您应该确保尝试() 返回 True 或 False。

      说真的,Johnsyweb 的答案可能更好。

      【讨论】:

      • 这是一个不错的方法。真的很有用。
      【解决方案6】:

      认为您正在将foo 映射到attempt 工作的所有项目上。所以attempt 是一个过滤器,很容易把它写成一个生成器:

      def attempted( items ):
          for item in items:
              try:
                  yield attempt( item )
              except Exception, e:
                  log(e)
      
      print [foo(bar) for bar in attempted( items )]
      

      【讨论】:

        【解决方案7】:

        我通常不会发布第二个答案,但如果您真的不喜欢 my first answer,这是一种替代方法。

        请记住,函数可以返回 tuple

        #!/usr/bin/env python
        
        def something_that_mail_fail(i):
            failed = False
            result = None
            try:
                result = 1.0 / (i % 4)
            except:
                failed = True # But we don't care
            return failed, result
        
        for i in range(20):
            failed, result = something_that_mail_fail(i)
            if failed:
                continue
            for rah in ['rah'] * 3:
                print(rah)
            print(result)
        

        我坚持认为try ... except ... else 是要走的路,但你不应该默默地忽略错误。 告诫购买者等等。

        【讨论】:

        • 除了返回元组 (True, None) 而不仅仅是 False,这与我在问题中提到的没有太大区别。这不是指示 for 循环从函数内部继续。
        • 对不起,我帮不了你。 Python 中没有goto。没错。
        【解决方案8】:

        将for循环放在try之外,除了块...简单... ;-)

        导入系统 如果 sys.version 中的“3.4”: 从 termcolor 导入有色

        定义列表属性(模块名称): '''在调用这个函数之前导入模块。s''' 对于索引,枚举中的方法(dir(module_name)): 尝试: 方法 = str(方法) 模块 = '电子邮件' 表达式 = 模块 + '.' + 方法 print('*' * len(表达式), '\n') print(str(index).upper() + '.',colored(expression.upper(), 'red'), ' ', eval( 表达式 ).dir() , '...' , '\n'2 ) print('' * len(表达式), '\n') print( eval( 表达式 + '.doc' ), '\n'*4, '描述结束:' + expression.upper(), '\n'*4) 除了(AttributeError,NameError): 继续 别的: 经过 最后: 通过

        【讨论】:

          【解决方案9】:

          编辑:删除我所说的所有愚蠢...

          最后的答案是重写整个东西,这样我就不需要那样编码了。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-09-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多