일반적으로 각 요소를 반복하고 계산하지 않고 파이썬의 반복자에 얼마나 많은 요소가 있는지 알 수있는 효율적인 방법이 있습니까?
답변
아니요. 불가능합니다.
예:
import random
def gen(n):
for i in xrange(n):
if random.randint(0, 1) == 0:
yield i
iterator = gen(10)
iterator
반복 할 때까지 길이를 알 수 없습니다.
답변
이 코드는 작동해야합니다.
>>> 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]
또한 이 작업을 수행 하면 이터레이터가 소진 될 수 있으며 추가로 시도하면 요소 가 표시 되지 않습니다 . 그것은 파이썬 반복자 디자인의 피할 수없는 결과입니다. 요소를 유지하려면 목록 또는 무언가에 요소를 저장해야합니다.
답변
아니요, 모든 방법을 사용하려면 모든 결과를 해결해야합니다. 넌 할 수있어
iter_length = len(list(iterable))
그러나 무한 반복자에서 실행하면 절대로 반환되지 않습니다. 또한 반복자를 소비하므로 내용을 사용하려면 재설정해야합니다.
해결하려는 실제 문제를 알려 주면 실제 목표를 달성하는 더 좋은 방법을 찾는 데 도움이 될 수 있습니다.
편집 :를 사용 list()
하면 전체 iterable을 한 번에 메모리로 읽을 수 있으므로 바람직하지 않습니다. 다른 방법은
sum(1 for _ in iterable)
다른 사람이 게시 한대로 메모리에 보관하지 마십시오.
답변
당신은 할 수 없다 (특정 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
).
답변
킨다 메소드를 확인할 수 는__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)
,
실행 성능 (메모리 소비 포함)으로 정렬하면 다음과 같이 놀라게됩니다.
“`
1 : test_list.py:8 : 0.492 KiB
gen = (i for i in data*1000); t0 = monotonic(); len(list(gen))
( ‘목록, 초’, 1.9684218849870376)
2 : test_list_compr.py:8 : 0.867 KiB
gen = (i for i in data*1000); t0 = monotonic(); len([i for i in gen])
( ‘list_compr, sec’, 2.5885991149989422)
3 : test_sum.py:8 : 0.859 KiB
gen = (i for i in data*1000); t0 = monotonic(); sum(1 for i in gen); t1 = monotonic()
( ‘sum, sec’, 3.441088170016883)
4 : more_itertools / more.py : 413 : 1.266 KiB
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)
5 : test_reduce.py:8 : 0.859 KiB
gen = (i for i in data*1000); t0 = monotonic(); reduce(lambda counter, i: counter + 1, gen, 0)
( ‘감소, 초’, 13.436614598002052)“`
따라서 len(list(gen))
가장 빈번하고 적은 메모리 소비