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

Python 如何在for循环迭代器中捕获异常,python,exception,Python,Exception,这是Python中的for循环: for_stmt ::= "for" target_list "in" expression_list ":" suite 通常,当从表达式列表生成值时引发异常,循环中止。是否有一种优雅的方法(除了使用而不是True或类似的方法重写循环)来捕获此异常并继续循环 以下是一个例子: import csv csv.field_size_limit(10) reader = csv.reader(open('test.csv', 'r')) for line i

这是Python中的
for
循环:

for_stmt ::=  "for" target_list "in" expression_list ":" suite
通常,当从
表达式列表
生成值时引发异常,循环中止。是否有一种优雅的方法(除了使用
而不是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

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

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

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

如果您的内部iterable可以在异常后继续,则只需包装一个简单的生成器:

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
,这意味着在将文件包装成
csv.reader
之前,先从文件中读取
n
行。在其他情况下,它可能意味着将
n
传递给构造函数。在其他情况下,就像上面的
raisinggenfunc
一样,这是不可能的。

很遗憾,这在纯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
人们会期望它在while循环的新迭代中继续,但事实并非如此

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

编辑:我没有直接回答你的问题。对于可以恢复的迭代器(这意味着它是一个C迭代器),按照abarnet所说的去做

编辑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元素之后生成迭代器,显然是可能的。如果您需要更完整的示例,请告诉我。

事实证明,如果您在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的旧版本中成功地使用了它

类似的问题:,但在这种情况下,我无法更改生成器。为什么不制作一个调用另一个生成器的生成器,但使用
try/except
?@StevenRumbalski我就是这么想的,但是使用
csv.reader
,它不是真的(我自己尝试过)。它会引发一个
\u csv.Error
,但是在
读卡器上再次调用
next()
将返回错误诱导行之后的下一个,并且它将像正常情况一样继续执行
StopIteration
除了:continue不起作用?@mehtunguh
raise
发生在
reader.next()
中,本质上是在for循环本身中调用的
print
是在事实之后进行的,因此它周围的
try/catch
将不起作用。如果内部iterable在异常后无法继续,则这将不起作用。(试着用中的
异常处理迭代器
替换
包装器
,你会发现你仍然得到
[1]
,而不是
[1,3]
),如果内部iterable可以继续,我认为它不会给更简单的迭代器增加任何东西。但是在OP的问题中,迭代器可以在异常之后继续,他希望能够使用一个或多个异常处理规则自定义异常的处理方式。这恰恰增加了这种能力。您可以针对每个处理程序情况一次性使用迭代器/生成器,但这只是重复代码。但您仍然不需要构建一个自定义迭代器来完成与生成器函数完全相同的工作。它需要更多的知识来编写,更容易出错,而且它有更多的样板文件阻碍了阅读。如果您有一组以上的日志记录/处理要以相同的模式实现,那么您最终会得到更多的样板文件,因为您必须在每种情况下重复此逻辑。这将定义如何处理异常的工作与迭代机制分离开来。所有用户必须知道的是异常的接口;所有关于迭代的细节都是隐藏的。您可以向生成器添加相同的
处理程序
回调;您仍然不需要显式地构建一个与迭代器协议匹配的类,这样它就可以做与生成器完全相同的事情,正如我们已经确定的那样,
csv.reader
是可连续的。泛化很少有害。除了停止迭代之外,它不应该是
:break
而不是
raise
?很漂亮!这帮助我跳过了编码错误的行。您的EDIT 2版本在错误发生后不会继续,那么这说明了什么?在出现错误之前,您进入迭代器的距离有多远。从这一点出发,理论上,另一个包装函数可以使一个新的迭代器在此之后继续运行。因此,我们的想法是使用异常将“游标”传递回调用代码。整洁的想法^^
SyntaxError:在python 2.7和3.x中,循环中的“continue”不正确
。这似乎是一个新的错误。在过去,我成功地使用它来传递错误行
reader=csv.reader
try:
   for row in reader:
      if row[0]=='type':
         datarows.append(row)
except: continue