Загрузка большого файла в Python

Я использую Python 2.6.2 [GCC 4.3.3], работающий на Ubuntu 9.04. Мне нужно прочитать большой файл данных (~ 1 ГБ,> 3 миллиона строк), строка за строкой, используя скрипт Python.

Я пробовал методы ниже, я считаю, что он использует очень большое пространство памяти (~ 3 ГБ)

for line in open('datafile','r').readlines(): process(line) 

или,

 for line in file(datafile): process(line) 

Есть ли лучший способ загрузить большой файл по строкам, скажем,

  • а) путем явного упоминания максимального количества строк, которые файл может загружать в любой момент времени в памяти? Или
  • б) путем загрузки его кусками размера, скажем, 1024 байта, при условии, что последняя строка упомянутого куска загружается полностью без усечения?

Несколько предложений дали методы, о которых я упоминал выше, и уже попытались, я пытаюсь понять, есть ли лучший способ справиться с этим. До сих пор мой поиск не был плодотворным. Я ценю вашу помощь.

p / s Я сделал некоторое профилирование памяти с помощью Heapy и не обнаружил утечек памяти в используемом мной коде Python.

Обновление 20 августа 2012 года, 16:41 (GMT + 1)

Пробовал оба подхода, предложенные Дж. Ф. Себастьяном, М. Милсоном и ЯмЧакком (файл данных является переменной)

 with open(datafile) as f: for line in f: process(line) 

Также,

 import fileinput for line in fileinput.input([datafile]): process(line) 

Странно, что оба они используют ~ 3 ГБ памяти, размер моего файла данных в этом тесте составляет 765,2 МБ, состоящий из 21 181 079 строк. Я вижу, что память увеличивается с течением времени (около 40-80 МБ шагов) до стабилизации на 3 ГБ.

Простейшее сомнение: необходимо ли очищать линию после использования?

Я сделал профилирование памяти с помощью Heapy, чтобы понять это лучше.

Профилирование уровня 1

 Partition of a set of 36043 objects. Total size = 5307704 bytes. Index Count % Size % Cumulative % Kind (class / dict of class) 0 15934 44 1301016 25 1301016 25 str 1 50 0 628400 12 1929416 36 dict of __main__.NodeStatistics 2 7584 21 620936 12 2550352 48 tuple 3 781 2 590776 11 3141128 59 dict (no owner) 4 90 0 278640 5 3419768 64 dict of module 5 2132 6 255840 5 3675608 69 types.CodeType 6 2059 6 247080 5 3922688 74 function 7 1716 5 245408 5 4168096 79 list 8 244 1 218512 4 4386608 83 type 9 224 1 213632 4 4600240 87 dict of type <104 more rows. Type eg '_.more' to view.> 

================================================== ==========

Профилирование уровня 2 для уровня 1-индекс 0

 Partition of a set of 15934 objects. Total size = 1301016 bytes. Index Count % Size % Cumulative % Referred Via: 0 2132 13 274232 21 274232 21 '.co_code' 1 2132 13 189832 15 464064 36 '.co_filename' 2 2024 13 114120 9 578184 44 '.co_lnotab' 3 247 2 110672 9 688856 53 "['__doc__']" 4 347 2 92456 7 781312 60 '.func_doc', '[0]' 5 448 3 27152 2 808464 62 '[1]' 6 260 2 15040 1 823504 63 '[2]' 7 201 1 11696 1 835200 64 '[3]' 8 188 1 11080 1 846280 65 '[0]' 9 157 1 8904 1 855184 66 '[4]' <4717 more rows. Type eg '_.more' to view.> 

Профилирование уровня 2 для уровня 1-индекс 1

 Partition of a set of 50 objects. Total size = 628400 bytes. Index Count % Size % Cumulative % Referred Via: 0 50 100 628400 100 628400 100 '.__dict__' 

Профилирование уровня 2 для уровня 1-индекс 2

 Partition of a set of 7584 objects. Total size = 620936 bytes. Index Count % Size % Cumulative % Referred Via: 0 1995 26 188160 30 188160 30 '.co_names' 1 2096 28 171072 28 359232 58 '.co_varnames' 2 2078 27 157608 25 516840 83 '.co_consts' 3 261 3 21616 3 538456 87 '.__mro__' 4 331 4 21488 3 559944 90 '.__bases__' 5 296 4 20216 3 580160 93 '.func_defaults' 6 55 1 3952 1 584112 94 '.co_freevars' 7 47 1 3456 1 587568 95 '.co_cellvars' 8 35 0 2560 0 590128 95 '[0]' 9 27 0 1952 0 592080 95 '.keys()[0]' <189 more rows. Type eg '_.more' to view.> 

Профилирование уровня 2 для уровня 1-индекс 3

 Partition of a set of 781 objects. Total size = 590776 bytes. Index Count % Size % Cumulative % Referred Via: 0 1 0 98584 17 98584 17 "['locale_alias']" 1 29 4 35768 6 134352 23 '[180]' 2 28 4 34720 6 169072 29 '[90]' 3 30 4 34512 6 203584 34 '[270]' 4 27 3 33672 6 237256 40 '[0]' 5 25 3 26968 5 264224 45 "['data']" 6 1 0 24856 4 289080 49 "['windows_locale']" 7 64 8 20224 3 309304 52 "['inters']" 8 64 8 17920 3 327224 55 "['galog']" 9 64 8 17920 3 345144 58 "['salog']" <84 more rows. Type eg '_.more' to view.> 

================================================== ==========

Уровень 3 Профилирование уровня 2-Индекс 0, Уровень 1-Индекс 0

 Partition of a set of 2132 objects. Total size = 274232 bytes. Index Count % Size % Cumulative % Referred Via: 0 2132 100 274232 100 274232 100 '.co_code' 

Уровень 3 Профилирование уровня 2-Индекс 0, Уровень 1-Индекс 1

 Partition of a set of 50 objects. Total size = 628400 bytes. Index Count % Size % Cumulative % Referred Via: 0 50 100 628400 100 628400 100 '.__dict__' 

Уровень 3 Профилирование уровня 2-Индекс 0, Уровень 1-Индекс 2

 Partition of a set of 1995 objects. Total size = 188160 bytes. Index Count % Size % Cumulative % Referred Via: 0 1995 100 188160 100 188160 100 '.co_names' 

Уровень 3 Профилирование уровня 2-Индекс 0, Уровень 1-Индекс 3

 Partition of a set of 1 object. Total size = 98584 bytes. Index Count % Size % Cumulative % Referred Via: 0 1 100 98584 100 98584 100 "['locale_alias']" 

Все еще устраняйте эту проблему.

Поделитесь со мной, если вы столкнулись с этим раньше.

Спасибо за вашу помощь.

Обновление 21 августа 2012, 01:55 (GMT + 1)

  1. mgilson, функция процесса используется для публикации файла трассировки сетевого симулятора 2 (NS2). Некоторые из строк в файле трассировки разделяются, как показано ниже. Я использую множество объектов, счетчиков, кортежей и словарей в сценарии python, чтобы узнать, как работает беспроводная сеть.
 s 1.231932886 _25_ AGT --- 0 exp 10 [0 0 0 0 YY] ------- [25:0 0:0 32 0 0] s 1.232087886 _25_ MAC --- 0 ARP 86 [0 ffffffff 67 806 YY] ------- [REQUEST 103/25 0/0] r 1.232776108 _42_ MAC --- 0 ARP 28 [0 ffffffff 67 806 YY] ------- [REQUEST 103/25 0/0] r 1.232776625 _34_ MAC --- 0 ARP 28 [0 ffffffff 67 806 YY] ------- [REQUEST 103/25 0/0] r 1.232776633 _9_ MAC --- 0 ARP 28 [0 ffffffff 67 806 YY] ------- [REQUEST 103/25 0/0] r 1.232776658 _0_ MAC --- 0 ARP 28 [0 ffffffff 67 806 YY] ------- [REQUEST 103/25 0/0] r 1.232856942 _35_ MAC --- 0 ARP 28 [0 ffffffff 64 806 YY] ------- [REQUEST 100/25 0/0] s 1.232871658 _0_ MAC --- 0 ARP 86 [13a 67 1 806 YY] ------- [REPLY 1/0 103/25] r 1.233096712 _29_ MAC --- 0 ARP 28 [0 ffffffff 66 806 YY] ------- [REQUEST 102/25 0/0] r 1.233097047 _4_ MAC --- 0 ARP 28 [0 ffffffff 66 806 YY] ------- [REQUEST 102/25 0/0] r 1.233097050 _26_ MAC --- 0 ARP 28 [0 ffffffff 66 806 YY] ------- [REQUEST 102/25 0/0] r 1.233097051 _1_ MAC --- 0 ARP 28 [0 ffffffff 66 806 YY] ------- [REQUEST 102/25 0/0] r 1.233109522 _25_ MAC --- 0 ARP 28 [13a 67 1 806 YY] ------- [REPLY 1/0 103/25] s 1.233119522 _25_ MAC --- 0 ACK 38 [0 1 67 0 YY] r 1.233236204 _17_ MAC --- 0 ARP 28 [0 ffffffff 65 806 YY] ------- [REQUEST 101/25 0/0] r 1.233236463 _20_ MAC --- 0 ARP 28 [0 ffffffff 65 806 YY] ------- [REQUEST 101/25 0/0] D 1.233236694 _18_ MAC COL 0 ARP 86 [0 ffffffff 65 806 67 1] ------- [REQUEST 101/25 0/0] 
  1. Цель профилирования 3 уровня с использованием Heapy – помочь мне сузить, какой объект (ы) съедает большую часть памяти. Как вы можете видеть, к сожалению, я не мог понять, какой из них нуждается в настройке, поскольку он слишком общий. Пример, который я знаю, хотя «dict of main .NodeStatistics» имеет только 50 объектов из 36043 (0,1%) объектов, но он занимает 12% от общей памяти, используемой для запуска скрипта, я не могу найти, какой конкретный словарь я бы нужно заглянуть.

  2. Я попытался реализовать предложение Дэвида Эйка, как показано ниже (фрагмент), пытаясь собрать мусор вручную на каждые 500 000 строк,

 import gc for i,line in enumerate(file(datafile)): if (i%500000==0): print '-----------This is line number', i collected = gc.collect() print "Garbage collector: collected %d objects." % (collected) 

К сожалению, использование памяти по-прежнему составляет 3 ГБ, а выход (фрагмент) – ниже,

 -----------This is line number 0 Garbage collector: collected 0 objects. -----------This is line number 500000 Garbage collector: collected 0 objects. 
  1. Реализованное предложение martineau, я вижу, что использование памяти теперь составляет 22 МБ от более ранних 3 ГБ! Что-то, чего я ожидал достичь. Странная вещь ниже,

Я делал то же самое профилирование памяти, что и раньше,

Профилирование уровня 1

 Partition of a set of 35474 objects. Total size = 5273376 bytes. Index Count % Size % Cumulative % Kind (class / dict of class) 0 15889 45 1283640 24 1283640 24 str 1 50 0 628400 12 1912040 36 dict of __main__.NodeStatistics 2 7559 21 617496 12 2529536 48 tuple 3 781 2 589240 11 3118776 59 dict (no owner) 4 90 0 278640 5 3397416 64 dict of module 5 2132 6 255840 5 3653256 69 types.CodeType 6 2059 6 247080 5 3900336 74 function 7 1716 5 245408 5 4145744 79 list 8 244 1 218512 4 4364256 83 type 9 224 1 213632 4 4577888 87 dict of type <104 more rows. Type eg '_.more' to view.> 

Сравнивая предыдущий вывод профилирования памяти с приведенным выше, str уменьшила 45 объектов (17376 байт), кортеж уменьшил 25 объектов (3440 байт) и dict (без владельца), хотя не изменил объект, он уменьшил 1536 байт объема памяти. Все остальные объекты одинаковы, включая dict main .NodeStatistics. Общее количество объектов – 35474. Небольшое сокращение объекта (0,2%) обеспечило 99,3% экономии памяти (22 МБ от 3 ГБ). Очень странно.

Если вы понимаете, хотя я знаю место голодания памяти, я все же могу сузить, какой из них вызывает кровотечение.

Будет продолжать исследовать это.

Спасибо всем указателям, используя эту возможность, чтобы много узнать о python, поскольку я не эксперт. Цените свое время, чтобы помочь мне.

Обновление 23 августа 2012, 00:01 (GMT + 1) – РЕШЕНИЕ

  1. Я продолжил отладку, используя минималистичный код на предложение мартини. Я начал добавлять коды в функцию процесса и наблюдать за кровотечением памяти.

  2. Я считаю, что память начинает истекать кровью, когда я добавляю класс, как показано ниже,

 class PacketStatistics(object): def __init__(self): self.event_id = 0 self.event_source = 0 self.event_dest = 0 ... 

Я использую 3 класса с 136 счетчиками.

  1. Обсудив этот вопрос с моим другом Густаво Карнейро, он предложил использовать слот для замены dict.

  2. Я преобразовал класс, как показано ниже,

 class PacketStatistics(object): __slots__ = ('event_id', 'event_source', 'event_dest',...) def __init__(self): self.event_id = 0 self.event_source = 0 self.event_dest = 0 ... 
  1. Когда я преобразовал все 3 класса, использование памяти 3 ГБ ранее стало 504 МБ. Огромное 80% экономии памяти!

  2. Ниже приведено профилирование памяти после преобразования dict в слот .

 Partition of a set of 36157 objects. Total size = 4758960 bytes. Index Count % Size % Cumulative % Kind (class / dict of class) 0 15966 44 1304424 27 1304424 27 str 1 7592 21 624776 13 1929200 41 tuple 2 780 2 587424 12 2516624 53 dict (no owner) 3 90 0 278640 6 2795264 59 dict of module 4 2132 6 255840 5 3051104 64 types.CodeType 5 2059 6 247080 5 3298184 69 function 6 1715 5 245336 5 3543520 74 list 7 225 1 232344 5 3775864 79 dict of type 8 244 1 223952 5 3999816 84 type 9 166 0 190096 4 4189912 88 dict of class <101 more rows. Type eg '_.more' to view.> 

The dict of __main__.NodeStatistics больше не входит в первую десятку.

Я доволен результатом и рад закрыть этот вопрос.

Спасибо за все ваше руководство. Поистине ценим это.

rgds Saravanan K

4 Solutions collect form web for “Загрузка большого файла в Python”

 with open('datafile') as f: for line in f: process(line) 

Это работает, потому что файлы являются итераторами, дающими 1 строку за раз, пока больше нет строк.

Модуль fileinput позволит вам читать строки за строкой, не загружая весь файл в память. pydocs

 import fileinput for line in fileinput.input(['myfile']): do_something(line) 

Пример кода, взятый из yak.net

Ответ @mgilson правильный. Простое решение имеет официальное упоминание, хотя (@HerrKaputt упомянул об этом в комментарии)

 file = open('datafile') for line in file: process(line) file.close() 

Это просто, пифонично и понятно. Если вы не понимаете, как это работает, просто используйте это.

Как упоминал другой плакат, это не создает большой список, например file.readlines (). Скорее, он отключает одну строку за раз, традиционно для файлов / труб Unix.

Если файл JSON, XML, CSV, genomics или любой другой известный формат, есть специализированные читатели, которые используют C-код напрямую и гораздо более оптимизированы как для скорости, так и для памяти, чем для парсинга на родном Python – избегайте синтаксического анализа его по возможности ,

Но в целом, советы по моему опыту:

  • Многопроцессорный пакет Python является фантастическим для управления подпроцессами, все утечки памяти исчезают, когда заканчивается подпроцесс.
  • запустите подпроцесс читателя как multiprocessing.Process и используйте multiprocessing.Pipe(duplex=True) для связи (отправьте имя файла и любые другие аргументы, затем прочитайте его stdout)
  • читайте в маленьких (но не крошечных) кусках, скажем, 64Kb-1Mb. Лучше для использования памяти, а также для реагирования на другие запущенные процессы / подпроцессы
  • python oauth 2.0 new fbsr facebook cookie, ошибка проверки кода проверки
  • Функция, которая принимает как расширенные аргументы, так и кортеж
  • Регистрация Facebook в Google App Engine
  • Python: значение опции -u?
  • Включение shlex в режим отладки
  • Ошибка округления Python с номерами с плавающей запятой
  • Хороший способ найти все комбинации, которые дают сумму N?
  • Как написать код Python, который может потребовать минимальную версию python?
  • Python - лучший язык программирования в мире.