python-вызываемый размер итератора?
Я просматриваю текстовый файл для определенной строки с помощью метода.
re.finditer(pattern,text)
Я хотел бы знать, когда это ничего не возвращает. это означает, что он ничего не мог найти в переданном тексте.
Я знаю, что вызываемые итераторы, имеют next()
и __iter__
Я хотел бы знать, могу ли я получить размер или узнать, не возвращает ли он строку, соответствующую моему шаблону.
6 ответов
EDIT 3: ответ @hynekcer намного лучше, чем это.
EDIT 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)
>>>
EDIT: вот более безопасная версия, но ее использование по-прежнему требует некоторой дисциплины. Это не совсем похоже на Пифию. Вы получите лучшее решение, если разместите весь соответствующий образец кода, который вы пытаетесь осуществлять.
>>> 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>
>>>
вот решение, которое использует меньше памяти, потому что он не сохраняет промежуточные результаты, как и другие решения, которые используют "список":
print sum(1 for _ in re.finditer(pattern, text))
все остальные решения имеют недостаток в том, что потребляют много памяти, если шаблон очень часто встречается в тексте, например pattern ' [a-z]'.
тест:
pattern = 'a'
text = 10240000 * 'a'
такое решение с sum(1 for ...)
использует примерно только память для текста как такового, то есть len(text)
байт. Этот предыдущие решения с помощью list
смогите использовать приблизительно 58 или 110 раз больше памяти чем необходимо. Это 580 МБ для 32-битного разрешения. 1.1 Гб для 64-битного Python 2.7.
нет извините итераторы не предназначены, чтобы знать длину они просто знают, что дальше, что делает их очень эффективными при прохождении коллекций. Хотя они быстрее, они не позволяют индексировать, в том числе зная длину коллекции.
вы можете получить количество элементов в итератор делать:
len( [m for m in re.finditer(pattern, text) ] )
итераторы являются итераторами, потому что они еще не создали последовательность. Этот приведенный выше код в основном извлекает каждый элемент из итератора, пока он не захочет остановиться в списке, а затем принимает длину этого массива. То, что было бы более эффективным памяти:
count = 0
for item in re.finditer(pattern, text):
count += 1
сложный подход к for-loop заключается в использовании reduce для эффективного подсчета элементов в iterator one на один. Это фактически то же самое, что и цикл for:
reduce( (lambda x, y : x + 1), myiterator, 0)
это в основном игнорирует y
перешел в reduce и просто добавляет один. Он инициализирует текущую сумму в 0
.
в то время как некоторые итераторы могут знать свою длину (например, они были созданы из строки или списка), большинство не делают и не могут. re.iter
является хорошим примером того, который не может знать его длину, пока он не будет завершен.
тем не менее, есть несколько различных способов улучшить ваш текущий код:
использовать
re.search
чтобы узнать, есть ли совпадения, используйтеre.finditer
для выполнения фактической обработки; илииспользовать a значение sentinel с
for
петли.
второй вариант выглядит примерно так:
match = empty = object()
for match in re.finditer(...):
# do some stuff
if match is empty:
# there were no matches
быстрым решением было бы превратить ваш итератор в список и проверить длину этого списка, но это может быть плохо для памяти, если слишком много результатов.
matches = list(re.finditer(pattern,text))
if matches:
do_something()
print("Found",len(matches),"matches")