Есть ли эффективный способ узнать, сколько элементов находится в итераторе в Python, в общем, без повторения каждого из них и подсчета?
Получение числа элементов в итераторе в Python
Ответ 1
Нет. Это невозможно.
Пример:
import random
def gen(n):
for i in xrange(n):
if random.randint(0, 1) == 0:
yield i
iterator = gen(10)
Длина iterator
неизвестна, пока вы не перейдете через нее.
Ответ 2
Этот код должен работать:
>>> iter = (i for i in range(50))
>>> sum(1 for _ in iter)
50
Хотя он выполняет итерацию по каждому элементу и подсчитывает их, это самый быстрый способ сделать это.
Ответ 3
Нет, любой метод потребует разрешения каждого результата. Вы можете сделать
iter_length = len(list(iterable))
но выполнение этого на бесконечном итераторе, конечно, никогда не вернется. Он также будет потреблять итератор, и он должен будет reset, если вы хотите использовать содержимое.
Рассказывая нам, какая реальная проблема, которую вы пытаетесь решить, может помочь нам найти лучший способ достичь вашей фактической цели.
Изменить: с помощью list()
будет считываться целая итерация в память сразу, что может быть нежелательным. Другой способ - сделать
sum(1 for _ in iterable)
как другой человек. Это позволит избежать его хранения в памяти.
Ответ 4
Вы не можете (кроме того, что тип конкретного итератора реализует некоторые конкретные методы, которые делают это возможным).
Как правило, вы можете считать элементы итератора только потреблением итератора. Один из наиболее эффективных способов:
import itertools
from collections import deque
def 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)
(для Python 3.x замените itertools.izip
на zip
).
Ответ 5
Любопытное. Вы можете проверить метод __length_hint__
, но предупреждайте, что (по крайней мере, до Python 3.4, как gsnedders помогает), недокументированная деталь реализации (следующее сообщение в потоке), который мог бы наилучшим образом исчезнуть или вызвать носовых демонов.
В противном случае нет. Итераторы - это всего лишь объект, который только выставляет метод next()
. Вы можете называть его столько раз, сколько требуется, и они могут или не могут в конечном итоге поднять StopIteration
. К счастью, это поведение в большинстве случаев является прозрачным для кодера.:)
Ответ 6
Итератор - это просто объект, который имеет указатель на следующий объект, который должен быть прочитан каким-то буфером или потоком, он, как LinkedList, где вы не знаете, сколько вещей у вас есть, пока вы не перейдете через них. Итераторы должны быть эффективными, потому что все, что они делают, это сказать вам, что будет дальше по ссылкам вместо использования индексации (но, как вы видели, вы теряете способность видеть, сколько записей будет дальше).
Ответ 7
Что касается вашего первоначального вопроса, ответ по-прежнему заключается в том, что вообще нет способа узнать длину итератора в Python.
Учитывая, что ваш вопрос мотивирован приложением библиотеки pysam, я могу дать более конкретный ответ: я являюсь вкладчиком в PySAM, и окончательный ответ заключается в том, что файлы SAM/BAM не предоставляют точное количество выровненных читает. Также эта информация легко доступна из файла индекса BAM. Самое лучшее, что можно сделать, это оценить приблизительное количество выравниваний, используя расположение указателя файла после прочтения ряда выравниваний и экстраполяции в зависимости от общего размера файла. Этого достаточно, чтобы реализовать индикатор выполнения, но не метод подсчета выравниваний в постоянное время.
Ответ 8
Мне нравится пакет cardinality, он очень легкий и пытается использовать максимально возможную реализацию, доступную в зависимости от итерации.
Использование:
>>> 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 else 0
Ответ 9
Есть два способа получить длину "что-то" на компьютере.
Первый способ - хранить счетчик - для этого требуется что-либо, что затрагивает файл/данные для его изменения (или класс, который предоставляет только интерфейсы, но он сводится к одному и тому же).
Другой способ - перебрать его и подсчитать, насколько он большой.
Ответ 10
Быстрый тест:
import collections
import itertools
def count_iter_items(iterable):
counter = itertools.count()
collections.deque(itertools.izip(iterable, counter), maxlen=0)
return next(counter)
def count_lencheck(iterable):
if hasattr(iterable, '__len__'):
return len(iterable)
d = collections.deque(enumerate(iterable, 1), maxlen=1)
return d[0][0] if d else 0
def count_sum(iter):
return sum(1 for _ in iter)
iter = (x for x in xrange(100))
%timeit count_iter_items(iter)
%timeit count_lencheck(iter)
%timeit sum(iter)
Результаты:
1000000 loops, best of 3: 553 ns per loop
1000000 loops, best of 3: 730 ns per loop
1000000 loops, best of 3: 246 ns per loop
т.е. простой count_sum - это путь.
Ответ 11
Общепринятой практикой является размещение этого типа информации в заголовке файла, а для pysam - доступ к этому. Я не знаю формат, но вы проверили API?
Как говорили другие, вы не можете знать длину от итератора.
Ответ 12
Это противоречит самому определению итератора, который является указателем на объект, а также информации о том, как перейти к следующему объекту.
Итератор не знает, сколько раз он сможет перебирать до конца. Это может быть бесконечно, поэтому бесконечность может быть вашим ответом.
Ответ 13
def count_iter(iter):
sum = 0
for _ in iter: sum += 1
return sum
Ответ 14
Хотя вообще невозможно сделать то, что было задано, все же часто полезно подсчитывать количество повторений итераций после их повторения. Для этого вы можете использовать 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