44

这是forPython 中的一个循环:

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

4 回答 4

43

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

def wrapper(gen):
  while True:
    try:
      yield next(gen)
    except StopIteration:
      break
    except Exception as e:
      print(e) # or whatever kind of logging you want

例如:

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上面一样,这是不可能的。

于 2012-11-30T22:41:14.667 回答
6

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

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
于 2012-11-30T22:33:29.873 回答
4

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

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

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

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

于 2016-03-20T05:46:34.523 回答
2

可以肯定的是,这在纯 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 模块在这里找到。我的 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 元素之后创建一个迭代器,那么显然是有可能的。如果您需要更完整的示例,请告诉我。

于 2012-11-30T23:04:11.850 回答