Как поймать исключение в итераторе цикла for
Проблема с обработкой исключений в цикле for
в Python
У меня есть цикл 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)
И вот содержимое файла test.csv
:
foo,bar,baz
xxx,veryverylong,yyy
abc,def,ghi
Данный код завершает выполнение при обработке второй строки. Я хотел бы найти способ пропускать или записывать ошибки для проблемных строк и продолжать выполнение цикла. Спасибо за помощь!
3 ответ(ов)
Если ваш внутренний итератор может продолжить свою работу после исключения, вам достаточно обернуть его в простой генератор:
def wrapper(gen):
while True:
try:
yield next(gen)
except StopIteration:
break
except Exception as e:
print(e) # или любой другой вид логирования, который вы предпочитаете
Пример использования:
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
, это просто невозможно.
Вы можете обернуть читатель в другой итератор, который будет обрабатывать исключения по вашему усмотрению.
class ExceptionHandlingIterator(object):
def __init__(self, iterable):
self._iter = iter(iterable)
self.handlers = []
def __iter__(self):
return self
def __next__(self):
try:
return next(self._iter)
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')))
# здесь можно прикрепить обработчики к читателю
for line in csv_reader:
print(line)
В этом примере мы создали класс ExceptionHandlingIterator
, который принимает любое итерируемое значение. Метод __next__
пытается вернуть следующий элемент, обрабатывая при этом исключения. Если возникает ошибка, вы можете зарегистрировать обработчики исключений для выполнения специфичных действий. Это дает вам гибкость в управлении исключениями, которые могут возникнуть при работе с данными (например, из CSV-файла).
Если вы используете csv.reader
в цикле for
, то можно обработать ошибки с помощью блока try-except
, и цикл продолжит свою работу. Вот пример:
import csv
datarows = []
with open('yourfile.csv', 'r') as file:
reader = csv.reader(file)
try:
for row in reader:
if row[0] == 'type':
datarows.append(row)
except Exception as e:
# Логируем ошибку, если нужно
print(f"Произошла ошибка: {e}")
Если в приведенном коде возникнет внутренняя ошибка, выполнение перейдет в блок except
, и продолжится итерация по следующей строке в CSV-файле.
Обновление: Учтите, что это может вызвать ошибку, как указано в комментариях. Хотя у меня был успешный опыт работы с данной конструкцией в более старых версиях Python 2.7, в более новых версиях рекомендуется использовать более точечную обработку исключений, чтобы избежать неожиданных ситуаций. Например, можно обрабатывать конкретные ошибки, такие как IndexError
, которые могут возникнуть, если в строке недостаточно элементов.
Как проверить, существует ли переменная?
Правильный способ использования try/except с модулем requests в Python?
Зачем в Python нужен блок "finally"?
Переизбрасывание исключения в Python с сохранением трассировки стека
Есть ли разница между поднятием экземпляра класса Exception и самого класса Exception?