如何在for循环迭代器中捕获异常

Pet*_*aut 30 python exception

这是forPython中的循环:

for_stmt ::=  "for" target_list "in" expression_list ":" suite
Run Code Online (Sandbox Code Playgroud)

通常,当从中产生值时expression_list会引发异常,循环将中止.是否有一种优雅的方式(没有重写循环使用while True或类似的东西)来捕获此异常并继续循环?

这是一个例子:

import csv

csv.field_size_limit(10)

reader = csv.reader(open('test.csv', 'r'))
for line in reader:
    print(line)
Run Code Online (Sandbox Code Playgroud)

用这个文件:

foo,bar,baz
xxx,veryverylong,yyy
abc,def,ghi
Run Code Online (Sandbox Code Playgroud)

这在第二行中止.我想要一种方法来跳过或记录失败的行并继续.

aba*_*ert 30

如果你的内部迭代可以在异常之后继续,那么你需要包装它是一个简单的生成器:

def wrapper(gen):
  while True:
    try:
      yield next(gen)
    except StopIteration:
      raise
    except Exception as e:
      print(e) # or whatever kind of logging you want
      pass
Run Code Online (Sandbox Code Playgroud)

例如:

In [9]: list(wrapper(csv.reader(open('test.csv', 'r'))))
field larger than field limit (10)
Out[9]: [['foo', 'bar', 'baz'], ['abc', 'def', 'ghi']]
Run Code Online (Sandbox Code Playgroud)

另一方面,如果异常后内部迭代器无法继续,则无法将其包装起来:

def raisinggenfunc():
    yield 1
    raise ValueError("spurious error")
    yield 3

In [11]: list(wrapper(raisinggenfunc()))
spurious error
Out[11]: [1]
Run Code Online (Sandbox Code Playgroud)

通过调用Python生成器函数或评估生成器表达式创建的任何生成器都不可恢复.

在这种情况下,您需要找到一些方法来创建一个恢复迭代的新迭代器.对于类似的东西csv.reader,这意味着n在将文件包装成文件之前从文件中读取行csv.reader.在其他情况下,它可能意味着传递n给构造函数.在其他情况下 - raisinggenfunc如上所述,这是不可能的.

  • 概括很少有害。 (3认同)
  • 不应该是`except StopIteration: break` 而不是`raise` 吗? (3认同)
  • 美丽的!这帮助我跳过编码错误的行 (3认同)
  • 谢谢你 - 聪明且解释得很好!但遗憾的是没有更优雅的方法来做到这一点。 (2认同)

Sil*_*Ray 5

您可以将阅读器包装在另一个迭代器中,然后根据需要处理异常。

class ExceptionHandlingIterator(object):
    def __init__(self, iterable):
        self._iter = iter(iterable)
        self.handlers = []
    def __iter__(self):
        return self
    def next(self):
        try:
            return self._iter.next()
        except StopIteration as e:
            raise e
        except Exception as e:
            for handler in self.handlers:
                handler(e)
            return self.next()

csv_reader = ExceptionHandlingIterator(csv.reader(open('test.csv', 'r'))
# attach handlers to the reader here
for line in csv_reader:
    print line
Run Code Online (Sandbox Code Playgroud)

  • 如果内部迭代在异常后无法继续,那么这将不起作用。(尝试在 http://pastebin.com/nZsyVfpe 中将 `wrapper` 替换为 `ExceptionHandlingIterator`,您会发现仍然得到 `[1]` 而不是 `[1, 3]`。) *可以*继续,我认为它不会为更简单的迭代器添加任何内容。 (2认同)