С другой стороны, def gen(): yield random.randint(0, 1)оно бесконечно, поэтому вы никогда не сможете найти длину, перебирая ее.
tgray
1
Итак, чтобы подтвердить очевидное: лучший способ получить «размер» итератора - просто посчитать, сколько раз вы прошли итерацию, верно? В таком случае это будет numIters = 0 ; while iterator: numIters +=1?
Майк Уильямсон
Интересно, так что это проблема остановки
Акабаба
231
Этот код должен работать:
>>> iter =(i for i in range(50))>>> sum(1for _ in iter)50
Хотя он выполняет итерацию по каждому элементу и считает их, это самый быстрый способ сделать это.
Это также работает, когда итератор не имеет элемента:
>>> sum(1for _ in range(0))0
Конечно, он работает вечно для бесконечного ввода, поэтому помните, что итераторы могут быть бесконечными:
>>> sum(1for _ in itertools.count())[nothing happens, forever]
Также имейте в виду, что при этом итератор будет исчерпан , и дальнейшие попытки его использования не будут видеть элементов . Это неизбежное следствие дизайна итератора Python. Если вы хотите сохранить элементы, вам придется хранить их в списке или что-то в этом роде.
Похоже, это именно то, что OP не хочет делать: перебирать итератор и считать.
Адам Кроссленд
36
Это эффективный способ подсчета элементов в итерируемом виде
капитан
9
Хотя это не то, что хочет OP, учитывая, что на его вопрос нет ответа, этот ответ избегает создания экземпляра списка, и он эмпирически быстрее по константе, чем метод сокращения, указанный выше.
Филипп Нордвол
5
Не могу помочь: это _ссылка на Perl $_? :)
Алоис Махдал
17
@AloisMahdal Нет. В Python принято использовать имя _для фиктивной переменной, значение которой вас не волнует.
Таймон
67
Нет, любой метод потребует от вас разрешения каждого результата. Ты можешь сделать
iter_length = len(list(iterable))
но выполнение этого на бесконечном итераторе, конечно, никогда не вернется. Он также будет использовать итератор, и его необходимо будет сбросить, если вы хотите использовать содержимое.
Если вы сообщите нам, какую реальную проблему вы пытаетесь решить, это может помочь вам найти более эффективный способ достижения вашей реальной цели.
Изменить: Использование list()будет читать все повторяемые в памяти сразу, что может быть нежелательно. Другой способ сделать
sum(1for _ in iterable)
как написал другой человек. Это позволит избежать сохранения в памяти.
проблема в том, что я читаю файл с "pysam", который имеет миллионы записей. Пысам возвращает итератор. Чтобы вычислить определенное количество, мне нужно знать, сколько операций чтения в файле, но мне не нужно читать каждое из них ... вот в чем проблема.
6
Я не пользователь pysam, но он, вероятно, читает файл "lazy". Это имеет смысл, потому что вы не хотите иметь большой файл в памяти. Так что, если вы должны знать, нет. записей до итерации, единственный способ - создать два итератора и использовать первый для подсчета элементов, а второй для чтения файла. КСТАТИ. Не используйте len(list(iterable))это загрузит все данные в память. Вы можете использовать: reduce(lambda x, _: x+1, iterable, 0). Изменить: Zonda333 код с суммой тоже хорошо.
Томаш Высоцкий
1
@ user248237: почему вы говорите, что вам нужно знать, сколько записей доступно для вычисления определенного количества? Вы можете просто прочитать их фиксированное количество и управлять случаем, когда их меньше, чем фиксированное количество (это действительно просто сделать с помощью iterslice). Есть ли еще одна причина, по которой вы должны прочитать все записи?
Крис
1
@Tomasz Обратите внимание, что Reduce устарело и исчезнет в Python 3 и выше.
Уилдак
7
@Wilduck: это не ушло, просто переехал вfunctools.reduce
Дейнит
33
Вы не можете (кроме типа конкретного итератора реализует некоторые конкретные методы, которые делают это возможным).
Как правило, вы можете считать элементы итератора только, потребляя итератор. Один из, вероятно, самых эффективных способов:
import itertoolsfrom collections import dequedef count_iter_items(iterable):"""
Consume an iterable not reading it into memory; return the number of items.
"""
counter = itertools.count()
deque(itertools.izip(iterable, counter), maxlen=0)# (consume at C speed)return next(counter)
+1: по сравнению со временем sum(1 for _ in iterator)это было почти в два раза быстрее.
августен
1
Точнее сказать, что он потребляет многократно, считывая каждый элемент в память и сразу выбрасывая его.
Rockallite
Важно отметить (что я упустил), что порядок аргументов zipимеет значение : если вы пройдете zip(counter, iterable), вы на самом деле получите на 1 больше, чем количество итераций!
Kye W Shi
очень хороший ответ. дал бы щедрость на это.
Реут
18
Вроде. Вы можете проверить __length_hint__метод, но имейте в виду, что (по крайней мере, до Python 3.4, как подсказывает gsnedders), это недокументированная деталь реализации ( после сообщения в теме ), которая может очень легко исчезнуть или вызвать назальных демонов.
В противном случае нет. Итераторы - это просто объект, который раскрывает только next()метод. Вы можете назвать это столько раз, сколько потребуется, и они могут или не могут в конечном итоге повысить StopIteration. К счастью, такое поведение в большинстве случаев прозрачно для кодировщика. :)
Это больше не относится к PEP 424 и Python 3.4. __length_hint__В настоящее время задокументировано, но это подсказка и не дает никаких гарантий точности.
gsnedders
12
Мне нравится пакет мощности для этого, он очень легкий и пытается использовать самую быструю из возможных реализаций в зависимости от итерируемого.
Использование:
>>>import cardinality
>>> cardinality.count([1,2,3])3>>> cardinality.count(i for i in range(500))500>>>def gen():...yield'hello'...yield'world'>>> cardinality.count(gen())2
Фактическая count()реализация выглядит следующим образом:
def count(iterable):if hasattr(iterable,'__len__'):return len(iterable)
d = collections.deque(enumerate(iterable,1), maxlen=1)return d[0][0]if d else0
Я предполагаю, что вы все еще можете перебирать итератор, если используете эту функцию, да?
Jcollum
12
Итак, для тех, кто хотел бы узнать краткое содержание этого обсуждения. Итоговые максимальные оценки для подсчета выражения генератора длиной 50 миллионов с использованием:
Можете ли вы объяснить, почему len(list(gen))следует использовать меньше памяти, чем подход, основанный на методе Reduce? Первый создает новый, listкоторый включает в себя распределение памяти, в то время как последний не должен. Так что я ожидаю, что последний будет более эффективным с точки зрения памяти. Кроме того, потребление памяти будет зависеть от типа элемента.
норманиус
К сведению: я могу воспроизвести для python 3.6.8 (на MacBookPro), что метод 1 превосходит другие методы с точки зрения времени выполнения (я пропустил метод 4).
Итератор - это просто объект, у которого есть указатель на следующий объект, который должен быть прочитан каким-либо буфером или потоком, он похож на LinkedList, где вы не знаете, сколько у вас есть вещей, пока не выполните их итерацию. Предполагается, что итераторы эффективны, потому что все, что они делают, - это сообщают вам, что дальше, по ссылкам, а не используют индексацию (но, как вы видели, вы теряете способность видеть, сколько записей дальше).
Итератор не похож на связанный список. Объект, возвращаемый из итератора, не указывает на следующий объект, и эти объекты (не обязательно) хранятся в памяти. Скорее, он может выдавать объект один за другим, основываясь на какой-либо внутренней логике (которая может быть, но не обязана, на основе сохраненного списка).
Том
1
@ Я использовал LinkedList в качестве примера, главным образом в том, что вы не знаете, сколько у вас есть, так как вы знаете только то, что дальше в некотором смысле (если что-то есть). Я прошу прощения, если моя формулировка кажется немного неправильной или я подразумевал, что они - одно и то же.
Иисус Рамос
8
Что касается вашего первоначального вопроса, ответ по-прежнему заключается в том, что в общем случае нет способа узнать длину итератора в Python.
Учитывая, что ваш вопрос мотивирован приложением библиотеки pysam, я могу дать более конкретный ответ: я участвую в PySAM, и однозначный ответ заключается в том, что файлы SAM / BAM не обеспечивают точного количества выровненных чтений. Также эта информация не легко доступна из индексного файла BAM. Лучшее, что можно сделать, - это оценить приблизительное количество выравниваний, используя расположение указателя файла после считывания ряда выравниваний и экстраполяции на основе общего размера файла. Этого достаточно, чтобы реализовать индикатор выполнения, но не метод подсчета выравниваний за постоянное время.
Есть два способа получить длину «чего-то» на компьютере.
Первый способ - сохранить счетчик - для его изменения требуется все, что касается файла / данных (или класс, который предоставляет только интерфейсы, но сводится к одному и тому же).
Другой способ - перебрать его и посчитать, насколько он велик.
Это противоречит самому определению итератора, который является указателем на объект, плюс информация о том, как добраться до следующего объекта.
Итератор не знает, сколько еще раз он сможет выполнить итерацию до завершения. Это может быть бесконечно, поэтому бесконечность может быть вашим ответом.
Это ничего не нарушает, и нет ничего плохого в применении предыдущих знаний при использовании итератора. Есть миллионы итераторов вокруг, где вы знаете, что количество элементов ограничено. Подумайте о простой фильтрации списка, вы можете легко указать максимальную длину, вы просто не знаете, сколько элементов на самом деле соответствуют условиям вашего фильтра. Желание узнать количество совпадающих элементов является допустимым приложением, не нарушающим ни одной загадочной идеи итератора.
Майкл
0
Хотя в общем и целом невозможно выполнить то, что было задано, все равно часто полезно подсчитывать, сколько элементов было повторено после их повторения. Для этого вы можете использовать jaraco.itertools.Counter или аналогичный. Вот пример использования Python 3 и rwt для загрузки пакета.
$ rwt -q jaraco.itertools ---q
>>>import jaraco.itertools
>>> items = jaraco.itertools.Counter(range(100))>>> _ = list(counted)>>> items.count
100>>>import random
>>>def gen(n):...for i in range(n):...if random.randint(0,1)==0:...yield i
...>>> items = jaraco.itertools.Counter(gen(100))>>> _ = list(counted)>>> items.count
48
Предположительно, вы хотите посчитать количество элементов без итераций, чтобы итератор не был исчерпан, и вы будете использовать его позже. Это возможно с copyилиdeepcopy
import copy
def get_iter_len(iterator):return sum(1for _ in copy.copy(iterator))###############################################
iterator = range(0,10)print(get_iter_len(iterator))if len(tuple(iterator))>1:print("Finding the length did not exhaust the iterator!")else:print("oh no! it's all gone")
Выход " Finding the length did not exhaust the iterator!"
По желанию (и неосознанно) вы можете скрыть встроенную lenфункцию следующим образом:
import copy
def len(obj,*, len=len):try:if hasattr(obj,"__len__"):
r = len(obj)elif hasattr(obj,"__next__"):
r = sum(1for _ in copy.copy(obj))else:
r = len(obj)finally:passreturn r
Диапазоны не являются итераторами. Есть некоторые типы итераторов, которые могут быть скопированы, но другие приведут к сбою этого кода с помощью TypeError (например, генераторов), а итерация через скопированный итератор может вызвать побочные эффекты дважды или вызвать произвольный сбой в коде, который, скажем, вернул mapитератор, ожидающий, что вызовы функций будут происходить только один раз.
Ответы:
Нет, это невозможно.
Пример:
Длина
iterator
неизвестна, пока вы не выполните итерацию.источник
def gen(): yield random.randint(0, 1)
оно бесконечно, поэтому вы никогда не сможете найти длину, перебирая ее.numIters = 0 ; while iterator: numIters +=1
?Этот код должен работать:
Хотя он выполняет итерацию по каждому элементу и считает их, это самый быстрый способ сделать это.
Это также работает, когда итератор не имеет элемента:
Конечно, он работает вечно для бесконечного ввода, поэтому помните, что итераторы могут быть бесконечными:
Также имейте в виду, что при этом итератор будет исчерпан , и дальнейшие попытки его использования не будут видеть элементов . Это неизбежное следствие дизайна итератора Python. Если вы хотите сохранить элементы, вам придется хранить их в списке или что-то в этом роде.
источник
_
ссылка на Perl$_
? :)_
для фиктивной переменной, значение которой вас не волнует.Нет, любой метод потребует от вас разрешения каждого результата. Ты можешь сделать
но выполнение этого на бесконечном итераторе, конечно, никогда не вернется. Он также будет использовать итератор, и его необходимо будет сбросить, если вы хотите использовать содержимое.
Если вы сообщите нам, какую реальную проблему вы пытаетесь решить, это может помочь вам найти более эффективный способ достижения вашей реальной цели.
Изменить: Использование
list()
будет читать все повторяемые в памяти сразу, что может быть нежелательно. Другой способ сделатькак написал другой человек. Это позволит избежать сохранения в памяти.
источник
len(list(iterable))
это загрузит все данные в память. Вы можете использовать:reduce(lambda x, _: x+1, iterable, 0)
. Изменить: Zonda333 код с суммой тоже хорошо.functools.reduce
Вы не можете (кроме типа конкретного итератора реализует некоторые конкретные методы, которые делают это возможным).
Как правило, вы можете считать элементы итератора только, потребляя итератор. Один из, вероятно, самых эффективных способов:
(Для Python 3.x заменить
itertools.izip
наzip
).источник
sum(1 for _ in iterator)
это было почти в два раза быстрее.zip
имеет значение : если вы пройдетеzip(counter, iterable)
, вы на самом деле получите на 1 больше, чем количество итераций!Вроде. Вы можете проверить
__length_hint__
метод, но имейте в виду, что (по крайней мере, до Python 3.4, как подсказывает gsnedders), это недокументированная деталь реализации ( после сообщения в теме ), которая может очень легко исчезнуть или вызвать назальных демонов.В противном случае нет. Итераторы - это просто объект, который раскрывает только
next()
метод. Вы можете назвать это столько раз, сколько потребуется, и они могут или не могут в конечном итоге повыситьStopIteration
. К счастью, такое поведение в большинстве случаев прозрачно для кодировщика. :)источник
__length_hint__
В настоящее время задокументировано, но это подсказка и не дает никаких гарантий точности.Мне нравится пакет мощности для этого, он очень легкий и пытается использовать самую быструю из возможных реализаций в зависимости от итерируемого.
Использование:
Фактическая
count()
реализация выглядит следующим образом:источник
Итак, для тех, кто хотел бы узнать краткое содержание этого обсуждения. Итоговые максимальные оценки для подсчета выражения генератора длиной 50 миллионов с использованием:
len(list(gen))
,len([_ for _ in gen])
,sum(1 for _ in gen),
ilen(gen)
(из more_itertool ),reduce(lambda c, i: c + 1, gen, 0)
,отсортированный по производительности выполнения (включая потребление памяти), удивит вас:
`` `
1: test_list.py:8: 0,492 КиБ
('list, sec', 1.9684218849870376)
2: test_list_compr.py:8: 0,867 КиБ
('list_compr, sec', 2.5885991149989422)
3: test_sum.py:8: 0,859 КиБ
(«сумма, сек», 3.441088170016883)
4: more_itertools / more.py: 413: 1,266 КиБ
('ilen, sec', 9.812256851990242)
5: test_reduce.py:8: 0,859 КиБ
(«уменьшить, сек», 13.436614598002052) `` `
Итак,
len(list(gen))
это самый частый и менее потребляемый объем памятиисточник
len(list(gen))
следует использовать меньше памяти, чем подход, основанный на методе Reduce? Первый создает новый,list
который включает в себя распределение памяти, в то время как последний не должен. Так что я ожидаю, что последний будет более эффективным с точки зрения памяти. Кроме того, потребление памяти будет зависеть от типа элемента.len(tuple(iterable))
может быть еще более эффективным: статья Нельсона МинараИтератор - это просто объект, у которого есть указатель на следующий объект, который должен быть прочитан каким-либо буфером или потоком, он похож на LinkedList, где вы не знаете, сколько у вас есть вещей, пока не выполните их итерацию. Предполагается, что итераторы эффективны, потому что все, что они делают, - это сообщают вам, что дальше, по ссылкам, а не используют индексацию (но, как вы видели, вы теряете способность видеть, сколько записей дальше).
источник
Что касается вашего первоначального вопроса, ответ по-прежнему заключается в том, что в общем случае нет способа узнать длину итератора в Python.
Учитывая, что ваш вопрос мотивирован приложением библиотеки pysam, я могу дать более конкретный ответ: я участвую в PySAM, и однозначный ответ заключается в том, что файлы SAM / BAM не обеспечивают точного количества выровненных чтений. Также эта информация не легко доступна из индексного файла BAM. Лучшее, что можно сделать, - это оценить приблизительное количество выравниваний, используя расположение указателя файла после считывания ряда выравниваний и экстраполяции на основе общего размера файла. Этого достаточно, чтобы реализовать индикатор выполнения, но не метод подсчета выравниваний за постоянное время.
источник
Быстрый тест:
Результаты:
Т.е. простой count_iter_items - это путь.
Настраиваем это для python3:
источник
Есть два способа получить длину «чего-то» на компьютере.
Первый способ - сохранить счетчик - для его изменения требуется все, что касается файла / данных (или класс, который предоставляет только интерфейсы, но сводится к одному и тому же).
Другой способ - перебрать его и посчитать, насколько он велик.
источник
Обычно такая информация помещается в заголовок файла, а pysam предоставляет вам доступ к этому. Я не знаю формат, но вы проверили API?
Как уже говорили другие, вы не можете узнать длину от итератора.
источник
Это противоречит самому определению итератора, который является указателем на объект, плюс информация о том, как добраться до следующего объекта.
Итератор не знает, сколько еще раз он сможет выполнить итерацию до завершения. Это может быть бесконечно, поэтому бесконечность может быть вашим ответом.
источник
Хотя в общем и целом невозможно выполнить то, что было задано, все равно часто полезно подсчитывать, сколько элементов было повторено после их повторения. Для этого вы можете использовать jaraco.itertools.Counter или аналогичный. Вот пример использования Python 3 и rwt для загрузки пакета.
источник
источник
Предположительно, вы хотите посчитать количество элементов без итераций, чтобы итератор не был исчерпан, и вы будете использовать его позже. Это возможно с
copy
илиdeepcopy
Выход "
Finding the length did not exhaust the iterator!
"По желанию (и неосознанно) вы можете скрыть встроенную
len
функцию следующим образом:источник
map
итератор, ожидающий, что вызовы функций будут происходить только один раз.