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

时间:2012-11-30 21:57:35

标签: python exception

这是Python中的for循环:

for_stmt ::=  "for" target_list "in" expression_list ":" suite

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

以下是一个例子:

import csv

csv.field_size_limit(10)

reader = csv.reader(open('test.csv', 'r'))
for line in reader:
    print(line)

使用此文件:

foo,bar,baz
xxx,veryverylong,yyy
abc,def,ghi

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

4 个答案:

答案 0 :(得分:29)

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

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

例如:

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

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

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

In [11]: list(wrapper(raisinggenfunc()))
spurious error
Out[11]: [1]

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

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

答案 1 :(得分:4)

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

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

答案 2 :(得分:3)

事实证明,如果你在for循环中使用csv.reader,那么你可以用try异常覆盖它,for循环将继续。这是一个示例:

reader=csv.reader
try:
   for row in reader:
      if row[0]=='type':
         datarows.append(row)
except: continue

如果此代码面临内部错误,它将跳转到except块并继续迭代CSV文件中的下一行。

更新:现在正如评论中所指出的那样出现错误,尽管我已在旧版本的2.7中成功使用它

答案 3 :(得分:1)

不幸的是,相当确定在纯Python中这是不可能的。

请注意以下代码:

def testIter(n):
    count = 0
    while count<n:
        try:
            for i in xrange(count,n):
                if i == 3:
                    raise Exception("Asdfas")
                count = count + 1
                yield i
        except:
            continue

这输出以下内容:

x = testIter(10)
x.next()  # 0
x.next()  # 1
x.next()  # 2
x.next()  # Exception: Asdfas
x.next()  # Exception: StopIteration

人们会期望它在while循环的新迭代中继续,但事实并非如此。

有些人表示csv.reader()会继续出错。我不想为它做一个测试用例,但如果确实如此,我怀疑是因为它是作为C模块实现的here。我的C不是太尖锐所以我没有深入研究它,但足以说我不认为这是可能的。

编辑:我没有直接回答你的问题。 abarnet在可以恢复的迭代器的情况下做什么(这意味着它是一个C迭代器)。

编辑2:实际上并非严格正确。

class myInformativeException(Exception):
    def __init__(self, count):
        self.count = count

def testIter(n):
    for i in xrange(n):
        if i==4:
            raise myInformativeException(i)
        yield i

def iterwrap(n):
    x = testIter(n)
    try:
        for i in x:
            yield i
    except myInformativeException as e:
        print "Error on ", e.count

打印:

0
1
2
3
Error on 4

所以,显然你可以在X元素之后创建一个迭代器。如果您需要更完整的示例,请与我们联系。