这是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
这在第二行中止。我想要一种方法来跳过或记录失败的行并继续。
答案 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元素之后创建一个迭代器。如果您需要更完整的示例,请与我们联系。