У меня есть следующая дилемма. Я разбираю огромные файлы CSV, которые теоретически могут содержать недопустимые записи, с python
. Чтобы быстро исправить проблему, я хотел бы видеть номера строк в сообщениях об ошибках. Однако, поскольку я разбираю много файлов и ошибок, очень редко, я не хочу, чтобы моя обработка ошибок добавила накладные расходы к основному конвейеру. Вот почему я не хотел бы использовать enumerate
или аналогичный подход.Значимые сообщения об ошибках IO без накладных расходов в Python
В двух словах, я ищу get_line_number
функции работать так:
with open('file.csv', 'r') as f:
for line in f:
try:
process(line)
except:
line_no = get_line_number(f)
raise RuntimeError('Error while processing the line ' + line_no)
Однако, это, кажется сложным, так как f.tell()
will not work в этом цикле.
EDIT:
Похоже, что накладные расходы являются весьма значительными. В моем реальном случае (что является болезненным, поскольку файлы представляют собой списки довольно коротких записей: одиночные float, пары int-float или пары string-int, а file.csv
- около 800 МБ и имеет около 80M строк), это около 2,5 секунд на файл, считанный для enumerate
. По некоторым причинам fileinput
является чрезвычайно медленно.
import timeit
s = """
with open('file.csv', 'r') as f:
for line in f:
pass
"""
print(timeit.repeat(s, number = 10, repeat = 3))
s = """
with open('file.csv', 'r') as f:
for idx, line in enumerate(f):
pass
"""
print(timeit.repeat(s, number = 10, repeat = 3))
s = """
count = 0
with open('file.csv', 'r') as f:
for line in f:
count += 1
"""
print(timeit.repeat(s, number = 10, repeat = 3))
setup = """
import fileinput
"""
s = """
for line in fileinput.input('file.csv'):
pass
"""
print(timeit.repeat(s, setup = setup, number = 10, repeat = 3))
выходы
[45.790788270998746, 44.88589363079518, 44.93949336092919]
[70.25306860171258, 70.28569177398458, 70.2074502906762]
[75.43606997421011, 74.39759518811479, 75.02027251804247]
[325.1898657102138, 321.0400970801711, 326.23809849238023]
EDIT 2:
Приблизиться к реальной ситуации. Предложение try-except
выходит за пределы цикла, чтобы уменьшить накладные расходы.
import timeit
setup = """
def process(line):
if float(line) < 0.5:
outliers += 1
"""
s = """
outliers = 0
with open('file.csv', 'r') as f:
for line in f:
process(line)
"""
print(timeit.repeat(s, setup = setup, number = 10, repeat = 3))
s = """
outliers = 0
with open('file.csv', 'r') as f:
try:
for idx, line in enumerate(f):
process(line)
except ValueError:
raise RuntimeError('Invalid value in line' + (idx + 1)) from None
"""
print(timeit.repeat(s, setup = setup, number = 10, repeat = 3))
выходы
[244.9097429071553, 242.84596176538616, 242.74369075801224
[293.32093235617504, 274.17732743313536, 274.00854821596295]
Итак, в моем случае, погоны из enumerate
составляет около 10%.
Итак, я должен спросить, проблема в том, что ваш пример, который будет работать, работает слишком медленно или что вы думаете, что он может работать слишком медленно? Какое влияние это оказывает на perf? Вы измеряли разницу в файле, который, как вы знаете, не имеет ошибок? –
Ничего себе, не ожидал бы, что 2х замедляется. Какое влияние оказывает на обработку вашего 'process (line)' call в 'try/catch'? –
И я тоже, но подставляя 'pass' для того, что бы действительно делалось с данными, не является самым справедливым из сравнений. Кроме того, всего десять байтов на строку файла csv довольно необычны. – nigel222