python - размер вызываемого итератора? - PullRequest
10 голосов
/ 27 июля 2010

Я просматриваю некоторый текстовый файл для определенной строки с помощью метода.

re.finditer(pattern,text) Я хотел бы знать, когда это ничего не возвращает.это означает, что в переданном тексте ничего не может быть найдено.

Я знаю, что вызываемые итераторы имеют next() и __iter__

.out, если он не возвращает строку, соответствующую моему шаблону.

Ответы [ 6 ]

17 голосов
/ 12 мая 2012

Это решение использует меньше памяти , поскольку оно не сохраняет промежуточные результаты, как и другие решения, использующие list:

sum(1 for _ in re.finditer(pattern, text))

Недостатком всех старых решений является то, что они занимают много памяти, если шаблон очень часто встречается в тексте, например шаблон '[a-z]'.

Контрольный пример:

pattern = 'a'
text = 10240000 * 'a'

Это решение с sum(1 for ...) использует приблизительно только память для текста как такового, то есть len(text) байтов. Предыдущие решения с list могут использовать примерно в 58 или 110 раз больше памяти, чем необходимо. Это 580 МБ для 32-разрядных, соответственно 1,1 ГБ для 64-битного Python 2.7.

7 голосов
/ 27 июля 2010

РЕДАКТИРОВАТЬ 3: Ответ @hynekcer намного лучше, чем этот.

РЕДАКТИРОВАТЬ 2: Это не будет работать, если у вас бесконечный итератор или тот, который использует слишком много гигабайт (в 2010 году 1 гигабайт по-прежнему остается большим объемом оперативной памяти / диска пространство) оперативной памяти / дискового пространства.

Вы уже видели хороший ответ, но вот дорогой хак, который вы можете использовать, если хотите съесть пирог и иметь его тоже :) Хитрость в том, что мы должны клонировать пирог, и когда вы закончите поедая, мы кладем его обратно в ту же коробку. Помните, что когда вы перебираете итератор, он обычно становится пустым или, по крайней мере, теряет ранее возвращенные значения.

>>> def getIterLength(iterator):
    temp = list(iterator)
    result = len(temp)
    iterator = iter(temp)
    return result

>>>
>>> f = xrange(20)
>>> f
xrange(20)
>>> 
>>> x = getIterLength(f)
>>> x
20
>>> f
xrange(20)
>>> 

РЕДАКТИРОВАТЬ: Вот более безопасная версия, но ее использование все еще требует некоторой дисциплины. Это не чувствует себя достаточно Pythonic. Вы получите лучшее решение, если разместите весь соответствующий пример кода, который вы пытаетесь реализовать.

>>> def getIterLenAndIter(iterator):
    temp = list(iterator)
    return len(temp), iter(temp)

>>> f = iter([1,2,3,7,8,9])
>>> f
<listiterator object at 0x02782890>
>>> l, f = getIterLenAndIter(f)
>>> 
>>> l
6
>>> f
<listiterator object at 0x02782610>
>>> 
5 голосов
/ 27 июля 2010

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

1 голос
/ 25 марта 2016

Хотя некоторые итераторы могут знать их длину (например, они были созданы из строки или списка), большинство не знает и не может.re.iter является хорошим примером того, кто не может знать его длину, пока он не закончил.

Однако есть несколько способов улучшить ваш текущий код:

  • используйте re.search, чтобы найти, есть ли совпадения, затем используйте re.finditer, чтобы выполнить фактическую обработку;или

  • использовать значение часового с циклом for.

Второй вариант выглядит примерно так:

match = empty = object()
for match in re.finditer(...):
    # do some stuff
if match is empty:
    # there were no matches
1 голос
/ 27 июля 2010

Вы можете получить количество элементов в итераторе, выполнив:

len( [m for m in re.finditer(pattern, text) ] )

Итераторы являются итераторами, поскольку они еще не сгенерировали последовательность. Приведенный выше код в основном извлекает каждый элемент из итератора до тех пор, пока он не захочет остановиться в списке, а затем измерить длину этого массива. Что-то, что было бы более эффективным с точки зрения памяти, будет:

count = 0
for item in re.finditer(pattern, text):
    count += 1

Хитрый подход к циклу for заключается в том, чтобы использовать сокращение для эффективного подсчета элементов в итераторе один за другим. Фактически это то же самое, что и цикл for:

reduce( (lambda x, y : x + 1), myiterator, 0)

Это в основном игнорирует y, переданное в Reduce, и просто добавляет один. Инициализирует текущую сумму на 0.

0 голосов
/ 27 июля 2010

Быстрое решение состоит в том, чтобы превратить ваш итератор в список и проверить длину этого списка, но это может быть плохо для памяти, если результатов слишком много.

matches = list(re.finditer(pattern,text))
if matches:
  do_something()
print("Found",len(matches),"matches")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...