답변:
아니요. 불가능합니다.
예:
import random
def gen(n):
for i in xrange(n):
if random.randint(0, 1) == 0:
yield i
iterator = gen(10)
iterator
반복 할 때까지 길이를 알 수 없습니다.
def gen(): yield random.randint(0, 1)
무한대이므로 반복하여 길이를 찾을 수 없습니다.
numIters = 0 ; while iterator: numIters +=1
?
이 코드는 작동해야합니다.
>>> iter = (i for i in range(50))
>>> sum(1 for _ in iter)
50
각 항목을 반복하고 계산하지만 가장 빠른 방법입니다.
반복자에 항목이없는 경우에도 작동합니다.
>>> sum(1 for _ in range(0))
0
물론 무한 입력을 위해 영원히 실행되므로 반복자는 무한 할 수 있습니다.
>>> sum(1 for _ in itertools.count())
[nothing happens, forever]
또한 이 작업을 수행 하면 이터레이터가 소진 될 수 있으며 추가로 시도하면 요소 가 표시 되지 않습니다 . 그것은 파이썬 반복자 디자인의 피할 수없는 결과입니다. 요소를 유지하려면 목록 또는 무언가에 요소를 저장해야합니다.
_
Perl의 참조 $_
입니까? :)
_
값이 중요하지 않은 더미 변수 의 이름을 사용하는 것이 일반적입니다 .
아니요, 모든 방법을 사용하려면 모든 결과를 해결해야합니다. 넌 할 수있어
iter_length = len(list(iterable))
그러나 무한 반복자에서 실행하면 절대로 반환되지 않습니다. 또한 반복자를 소비하므로 내용을 사용하려면 재설정해야합니다.
해결하려는 실제 문제를 알려 주면 실제 목표를 달성하는 더 좋은 방법을 찾는 데 도움이 될 수 있습니다.
편집 :를 사용 list()
하면 전체 iterable을 한 번에 메모리로 읽을 수 있으므로 바람직하지 않습니다. 다른 방법은
sum(1 for _ in iterable)
다른 사람이 게시 한대로 메모리에 보관하지 마십시오.
len(list(iterable))
모든 데이터가 메모리에로드됩니다. 사용할 수 있습니다 : reduce(lambda x, _: x+1, iterable, 0)
. 편집 : 합계 Zonda333 코드도 좋습니다.
functools.reduce
당신은 할 수 없다 (특정 iterator의 유형이 그것을 가능하게하는 특정 메소드를 구현하는 것을 제외하고).
일반적으로 반복자를 소비하여 반복자 항목 만 계산할 수 있습니다. 아마도 가장 효율적인 방법 중 하나입니다.
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)
(들어 파이썬은 대체 3.X itertools.izip
와 함께 zip
).
sum(1 for _ in iterator)
하면 거의 두 배나 빠릅니다.
zip
문제는 당신이 전달하는 경우 : zip(counter, iterable)
당신이 실제로 반복 가능한 수보다 더 많은 일을 얻을 것이다!
킨다 메소드를 확인할 수 는__length_hint__
있지만 (최소한 Python 3.4, gsnedders가 유용하게 지적했듯이) 문서화되지 않은 구현 세부 사항입니다 ( 스레드의 메시지 다음) )이며 대신 비강 악마를 소멸하거나 소환 할 수 .
그렇지 않으면 아닙니다. 반복자는 next()
메서드 만 노출하는 개체 일뿐 입니다. 필요에 따라 여러 번 호출 할 수 있으며 결국에는 올리거나 올리지 않을 수 있습니다 StopIteration
. 운 좋게도이 동작은 대부분 코더에게 투명합니다. :)
나는 카디널리티를 좋아한다 이것을 위해 패키지를 . 그것은 매우 가볍고 iterable에 따라 가능한 가장 빠른 구현을 사용하려고 시도한다.
용법:
>>> 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
따라서 그 토론의 요약을 알고 싶은 사람들을 위해. 최종 최고 점수는 다음을 사용하여 5 천만 길이의 발전기 표현식을 계산합니다.
len(list(gen))
, len([_ for _ in gen])
, sum(1 for _ in gen),
ilen(gen)
( more_itertool에서 ),reduce(lambda c, i: c + 1, gen, 0)
, 실행 성능 (메모리 소비 포함)으로 정렬하면 다음과 같이 놀라게됩니다.
```
gen = (i for i in data*1000); t0 = monotonic(); len(list(gen))
( '목록, 초', 1.9684218849870376)
gen = (i for i in data*1000); t0 = monotonic(); len([i for i in gen])
( 'list_compr, sec', 2.5885991149989422)
gen = (i for i in data*1000); t0 = monotonic(); sum(1 for i in gen); t1 = monotonic()
( 'sum, sec', 3.441088170016883)
d = deque(enumerate(iterable, 1), maxlen=1)
test_ilen.py:10: 0.875 KiB
gen = (i for i in data*1000); t0 = monotonic(); ilen(gen)
( 'ilen, sec', 9.812256851990242)
gen = (i for i in data*1000); t0 = monotonic(); reduce(lambda counter, i: counter + 1, gen, 0)
( '감소, 초', 13.436614598002052)```
따라서 len(list(gen))
가장 빈번하고 적은 메모리 소비
len(list(gen))
감소를 기반으로 한 접근 방식보다 적은 메모리를 사용해야하는 ? 전자 list
는 메모리 할당과 관련된 새로운 것을 생성 하지만 후자는 메모리 할당을 포함하지 않아야합니다. 따라서 후자는 메모리 효율성이 더 높을 것으로 기대합니다. 또한 메모리 소비는 요소 유형에 따라 다릅니다.
len(tuple(iterable))
훨씬 더 효율적일 수 있습니다. 넬슨 Minar입니다 문서,
반복자는 일종의 버퍼 또는 스트림에서 읽을 다음 객체에 대한 포인터가있는 객체 일뿐입니다. 반복 할 때까지 얼마나 많은 것들을 알지 못하는 LinkedList와 같습니다. 이터레이터는 인덱스를 사용하는 대신 참조로 다음에 나오는 것을 알려주기 때문에 효율적입니다 (그러나 몇 개의 항목이 있는지 볼 수있는 능력을 잃어 버렸습니다).
원래의 질문과 관련하여 대답은 여전히 일반적으로 파이썬에서 반복자의 길이를 알 수있는 방법이 없다는 것입니다.
pysam 라이브러리의 응용 프로그램에서 질문에 대한 동기가 부여되면보다 구체적인 답변을 줄 수 있습니다. 저는 PySAM에 기여하고 결정적인 답변은 SAM / BAM 파일이 정확한 정렬 된 읽기 수를 제공하지 않는다는 것입니다. 이 정보는 BAM 인덱스 파일에서도 쉽게 사용할 수 없습니다. 가장 좋은 방법은 여러 정렬을 읽은 후 파일의 전체 크기를 기반으로 외삽 한 후 파일 포인터의 위치를 사용하여 대략적인 정렬 수를 추정하는 것입니다. 이것은 진행률 표시 줄을 구현하기에 충분하지만 일정한 시간에 정렬을 계산하는 방법은 아닙니다.
빠른 벤치 마크 :
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(iterable):
return sum(1 for _ in iterable)
iter = lambda y: (x for x in xrange(y))
%timeit count_iter_items(iter(1000))
%timeit count_lencheck(iter(1000))
%timeit count_sum(iter(1000))
결과 :
10000 loops, best of 3: 37.2 µs per loop
10000 loops, best of 3: 47.6 µs per loop
10000 loops, best of 3: 61 µs per loop
즉 간단한 count_iter_items가 방법입니다.
python3에 이것을 조정 :
61.9 µs ± 275 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
74.4 µs ± 190 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
82.6 µs ± 164 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
이것은 객체에 대한 포인터 인 iterator의 정의와 다음 객체에 도달하는 방법에 대한 정보에 위배됩니다.
반복자는 종료 할 때까지 반복 할 수있는 횟수를 모릅니다. 이것은 무한대 일 수 있으므로 무한대가 답이 될 수 있습니다.
일반적으로 요청 된 내용을 수행하는 것은 불가능하지만, 반복 된 후 반복 된 항목 수를 세는 것이 여전히 유용 합니다. 이를 위해 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(1 for _ 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(1 for _ in copy.copy(obj))
else:
r = len(obj)
finally:
pass
return r
map
결과 함수 호출이 한 번만 발생할 것으로 예상 하는 반복자를 반환했습니다 .