Как поймать исключение в итераторе цикла for
Это цикл for
в Python:
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
Это прерывается во второй строке. Я бы хотел, чтобы пропустить или зарегистрировать неудачные строки и продолжить.
Ответы
Ответ 1
Если ваш внутренний итерабель может быть продолжен после исключения, все, что вам нужно обернуть, это тривиальный генератор:
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
выше, это просто невозможно.
Ответ 2
Оказывается, если вы используете csv.reader
в цикле for, вы можете покрыть это с помощью исключения try и цикл for будет продолжен. Здесь образец:
reader=csv.reader
try:
for row in reader:
if row[0]=='type':
datarows.append(row)
except: continue
Если этот код сталкивается с внутренней ошибкой, он переходит к исключающему блоку и продолжает итерацию для следующей строки в файле CSV.
Ответ 3
Вы можете обернуть читателя в другой итератор, который затем обрабатывает исключения, как вам угодно.
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
Ответ 4
Несомненно, что это невозможно в чистом 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 не слишком резкий, поэтому я не слишком разбираюсь в этом, но достаточно сказать, что я не думаю, что это возможно.
EDIT: я не ответил на ваш вопрос напрямую. Сделайте то, что 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 элементов. Дайте мне знать, если вам нужен более полный пример.