ProgramingTip

생성기 / 반복자에서 항목 수를 계산하는 가장 짧은 방법은 무엇입니까?

bestdevel 2020. 12. 6. 21:51
반응형

생성기 / 반복자에서 항목 수를 계산하는 가장 짧은 방법은 무엇입니까?


요소 자체를 신경 쓰지 않고 반복 가능한 항목의 수를 그것을 얻을 비단뱀적인 방법은 무엇입니까? 지금은

def ilen(it):
    return sum(itertools.imap(lambda _: 1, it))    # or just map in Python 3

나는 그것이 lambda바로 lambda _: 1예쁘지 않다는 것을 이해 합니다.

(이의 사용 사례는 정규식과 일치하는 텍스트 파일의 줄 수를 계산하는 것 grep -c입니다.)


일반적인 방법은

sum(1 for i in it)

더 큰 입력에 대한 스왑 스 래싱 및 재 할당 오버 헤드를 방지하기 위해 sum(1 for i in it)고정 메모리 오버 헤드 동작 (과 달리 len(list(it)))을 유지하면서 iterable이 길 수있을 때보 다 의미있게 더 빠른 방법 (이터 러블이 짧을 때 의미있게 느리지 발생 ) :

# On Python 2 only, get zip that lazily generates results instead of returning list
from future_builtins import zip

from collections import deque
from itertools import count

def ilen(it):
    # Make a stateful counting iterator
    cnt = count()
    # zip it with the input iterator, then drain until input exhausted at C level
    deque(zip(it, cnt), 0) # cnt must be second zip arg to avoid advancing too far
    # Since count 0 based, the next value is the count
    return next(cnt)

매입 len(list(it))그 CPython의 C에 코드의 반복 수행 ( deque, countzip모든 C로 구현 됨); 루프 당 바이트 코드 실행을 피하는 것이 일반적으로 CPython 성능의 핵심입니다.

성능을 비교하기위한 공정한 테스트 케이스를 찾는 것은 놀랍도록 어렵습니다 ( 임의의 입력 반복 가능에 사용할 수없는 것 같은 list치트 __length_hint__, 종종 itertools제공하지 않는 함수 __length_hint__에는 값이 각 루프에서 반환 될 때 더 빠른 작동하는 특수 작동 모드) 가 있습니다) 다음 값이있는 요청하기 전에 해제 해제 deque와 함께 maxlen=0할 것입니다). 사용한 테스트 케이스 내가는 입력 입력을 받아 특별한 itertools반환 컨테이너 최적화 가없는 C 생성기를 반환 레벨하는 생성기 함수를 만들 거나 __length_hint__파이썬 3.3을 사용하는 것입니다 yield from.

def no_opt_iter(it):
    yield from it

그런 다음 ipython %timeit매직을 사용 합니다 (100을 다른 상수로 대체).

>>> %%timeit -r5 fakeinput = (0,) * 100
... ilen(no_opt_iter(fakeinput))

입력이 len(list(it))메모리 문제를 유발할 만큼 충분히 크지 않은 경우 Python 3.5 x64를 실행하는 Linux 상자에서 내 솔루션은 def ilen(it): return len(list(it))입력 길이에 관계없이.

가장 작은 입력의 경우 deque/ zip/ count/ 호출하는 설정 비용 nextdef ilen(it): sum(1 for x in it)(길이 0 입력의 경우 내 컴퓨터에서 약 200ns 더 많은, 간단한 sum접근 방식에 비해 33 % 증가) 이 방법보다 무한히 더 오래 있습니다. 더 긴 입력, 추가 요소 당 약 절반의 시간으로 실행됩니다. 길이 5 입력의 경우 비용은 동일하며 길이 50-100 범위의 어딘가에서는 실제 작업에 초기 오버 헤드가 눈에 많이 지 연습합니다. sum접근 방식은 두 배 오래 전시합니다.

기본적으로 메모리 사용이 중요하거나 입력에 크기가없고 간결함보다 속도가 더 중요하다면 솔루션을 사용하십시오. 입력이 제한되고 작은 경우 len(list(it))아마도 가장 좋을 제한, 제한이 없지만 단순성 / 간결성이 중요하다면 sum(1 for x in it).


짧은 방법은 다음과 가변합니다.

def ilen(it):
    return len(list(it))

많은 요소 (예 : 수만 개 이상)를 생성하는 경우 목록에 넣는 것이 성능 문제가 될 수 있습니다. 그러나 대부분의 경우 성능이 중요하지 않은 아이디어의 간단한 표현입니다.


more_itertoolsilen도구 를 구현하는 라이브러리입니다 .pip install more_itertools

import more_itertools as mit


mit.ilen(x for x in range(10))
# 10

나는 이것에 대한 카디널리티 패키지를 좋아한다 . 그것은 매우 가볍고 반복 가능한 것에 따라 가능한 가장 빠른 구현을 사용하려고한다.

용법:

>>> 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

이것들은 내 선택이 될 것입니다.

print(len([*gen]))
print(len(list(gen)))

참고 URL : https://stackoverflow.com/questions/5384570/whats-the-shortest-way-to-count-the-number-of-items-in-a-generator-iterator

반응형