[python] 파이썬에서 이터레이터의 요소 수 얻기

일반적으로 각 요소를 반복하고 계산하지 않고 파이썬의 반복자에 얼마나 많은 요소가 있는지 알 수있는 효율적인 방법이 있습니까?



답변

아니요. 불가능합니다.

예:

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))가장 빈번하고 적은 메모리 소비