programing

생성자 표현식 vs. 목록 통합

kingscode 2022. 10. 10. 21:49
반응형

생성자 표현식 vs. 목록 통합

Python에서 생성기 식을 사용해야 할 때와 목록 압축을 사용해야 할 때는 언제입니까?

# Generator expression
(x*2 for x in range(256))

# List comprehension
[x*2 for x in range(256)]

John의 답변은 훌륭합니다(어떤 것에 대해 여러 번 반복하고 싶을 때 목록 이해가 더 좋습니다).그러나 목록 방법을 사용하려면 목록을 사용해야 합니다.예를 들어, 다음 코드는 작동하지 않습니다.

def gen():
    return (something for something in get_some_stuff())

print gen()[:2]     # generators don't support indexing or slicing
print [5,6] + gen() # generators can't be added to lists

기본적으로 한 번만 반복하는 경우 생성기 식을 사용합니다.생성된 결과를 저장하고 사용하려면 목록 이해 기능을 사용하는 것이 좋습니다.

퍼포먼스를 선택하는 가장 일반적인 이유는 퍼포먼스이기 때문에 걱정하지 말고 한쪽을 선택하는 것입니다.프로그램이 너무 느리게 실행되고 있는 것을 알게 되면 다시 돌아가서 코드 조정에 대해 걱정해야 합니다.

생성기 식 또는 목록 이해에 대해 반복해도 동일한 작업이 수행됩니다.그러나 목록 이해는 먼저 전체 목록을 메모리에 생성하고 생성기 식은 항목을 즉시 생성하므로 매우 큰(및 무한대!) 시퀀스에 사용할 수 있습니다.

결과를 여러 번 반복해야 하거나 속도가 가장 중요한 경우 목록 통합을 사용합니다.범위가 크거나 무한할 경우 제너레이터 식을 사용합니다.

자세한 내용은 생성기 식 및 목록 통합을 참조하십시오.

중요한 점은 목록 이해는 새로운 목록을 만든다는 것이다.제너레이터는 사용자가 비트를 소비할 때 소스 재료를 즉시 "필터링"하는 반복 가능한 개체를 생성합니다.

"hugefile.txt"라는 2TB 로그 파일이 있고 "ENTRI"로 시작하는 모든 줄의 내용과 길이를 원한다고 가정해 보십시오.

먼저 목록 이해도를 작성합니다.

logfile = open("hugefile.txt","r")
entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")]

그러면 파일 전체가 슬래핑되고 각 행이 처리되며 일치하는 행이 배열에 저장됩니다.따라서 이 어레이는 최대 2TB의 콘텐츠를 포함할 수 있습니다.RAM 용량이 너무 커서 목적에 적합하지 않을 수 있습니다.

대신 생성기를 사용하여 컨텐츠에 "필터"를 적용할 수 있습니다.결과에 대해 반복을 시작할 때까지 데이터는 실제로 읽히지 않습니다.

logfile = open("hugefile.txt","r")
entry_lines = ((line,len(line)) for line in logfile if line.startswith("ENTRY"))

우리 파일에서는 아직 한 줄도 읽히지 않았다.실제로 결과를 더 자세히 필터링하고 싶다고 가정해 보겠습니다.

long_entries = ((line,length) for (line,length) in entry_lines if length > 80)

아직 읽혀진 것은 없지만, 원하는 대로 데이터를 처리하는 발전기를 2대 지정했습니다.

필터링된 행을 다른 파일에 씁니다.

outfile = open("filtered.txt","a")
for entry,length in long_entries:
    outfile.write(entry)

이제 입력 파일을 읽겠습니다.우리 회사로서for 회선,즉 "Loop"을 합니다.long_entries는 발 from from 합니다.entry_lines합니다.그리고 그 다음에entry_lines는 회선을 합니다(logfile츠미야

따라서 데이터를 완전히 채워진 목록 형태로 출력 함수에 "푸시"하는 대신 출력 함수에 필요한 경우에만 데이터를 "풀"할 수 있는 방법을 제공합니다.이 경우 훨씬 효율적이지만 유연하지는 않습니다.생성기는 단방향, 원패스입니다.읽은 로그 파일의 데이터는 즉시 파기되므로 이전 행으로 돌아갈 수 없습니다.한편, 데이터를 보관하고 나면 데이터를 보관할 필요가 없습니다.

제너레이터 식의 장점은 전체 목록을 한 번에 작성하지 않기 때문에 메모리 사용량이 적다는 것입니다.생성기 표현식은 목록이 중간 매개체(예: 결과 합산 또는 결과에서 딕트 생성)일 때 가장 적합합니다.

예를 들어 다음과 같습니다.

sum(x*2 for x in xrange(256))

dict( (k, some_func(k)) for k in some_list_of_keys )

그 장점은 리스트가 완전히 생성되지 않기 때문에 메모리 사용량이 적다는 것입니다(또한 더 빨라야 합니다).

그러나 원하는 최종 제품이 목록일 경우 목록 통합을 사용해야 합니다.생성된 목록을 원하기 때문에 생성기 식을 사용하여 어떤 메모리도 저장하지 않습니다.또한 정렬 또는 역방향 목록 기능을 사용할 수 있다는 이점도 있습니다.

예를 들어 다음과 같습니다.

reversed( [x*2 for x in xrange(256)] )

목록과 같은 가변 개체에서 생성기를 생성할 경우 생성기를 생성할 때가 아니라 생성기를 사용할 때 목록 상태에 따라 생성기가 평가된다는 점에 유의하십시오.

>>> mylist = ["a", "b", "c"]
>>> gen = (elem + "1" for elem in mylist)
>>> mylist.clear()
>>> for x in gen: print (x)
# nothing

목록이 수정될 가능성이 있지만(또는 목록 내의 가변 개체) 생성 시 상태가 필요한 경우 대신 목록 이해를 사용해야 합니다.

Python 3.7:

목록 수집이 더 빠릅니다.

여기에 이미지 설명 입력

제너레이터가 메모리 효율이 향상됩니다. 여기에 이미지 설명 입력

다른 모든 기업이 말했듯이 무한 데이터 확장을 원한다면 결국 발전기가 필요합니다.속도가 필요한 비교적 정적인 중소규모 작업의 경우 목록 이해가 가장 좋습니다.

때로는 반복 도구의 티 함수를 사용하지 않고 독립적으로 사용할 수 있는 동일한 발전기에 대해 여러 개의 반복기를 반환할 수도 있습니다.

하둡 민스미트 모듈을 사용하고 있습니다.이것은 주의할 만한 좋은 예라고 생각합니다.

import mincemeat

def mapfn(k,v):
    for w in v:
        yield 'sum',w
        #yield 'count',1


def reducefn(k,v): 
    r1=sum(v)
    r2=len(v)
    print r2
    m=r1/r2
    std=0
    for i in range(r2):
       std+=pow(abs(v[i]-m),2)  
    res=pow((std/r2),0.5)
    return r1,r2,res

여기서 생성기는 텍스트 파일에서 숫자(최대 15GB)를 가져와 Hadoop의 지도 축소를 사용하여 해당 숫자에 간단한 계산을 적용합니다.항복 함수를 사용하지 않고 목록 이해를 사용했다면 (공간 복잡도는 말할 것도 없고) 합계와 평균을 계산하는 데 훨씬 더 오랜 시간이 걸렸을 것입니다.

Hadoop은 Generator의 모든 장점을 활용하는 좋은 예입니다.

목록 수집은 필수적이지만 생성기는 느립니다.

목록 통합에서는 모든 개체가 즉시 생성되므로 목록을 만들고 반환하는 데 시간이 더 오래 걸립니다.된다.next().에 대해서next()생성기 개체가 생성되고 즉시 반환됩니다.

개체가 이미 생성되었기 때문에 목록 통합에서 반복이 더 빠릅니다.

목록 이해 및 생성 식에 있는 모든 요소를 반복하면 시간 성능이 거의 동일합니다.제너레이터 식 Return Generator 개체가 바로 생성되더라도 모든 요소가 생성되지는 않습니다.새 요소를 반복할 때마다 새 요소가 생성되어 반환됩니다.

그러나 모든 요소를 반복하지 않으면 생성기가 더 효율적입니다.수백만 개의 항목을 포함하는 목록 통합을 만들어야 하는데 이 중 10개 항목만 사용하고 있다고 가정해 보겠습니다.아직 수백만 개의 아이템을 만들어야 합니다.당신은 단지 10개만 사용하기 위해 수백만 개의 아이템을 만들기 위해 수백만 개의 계산을 하는 데 시간을 낭비하고 있을 뿐입니다.또는 수백만 개의 API 요청을 했지만 결국 10개만 사용하게 되는 경우.제너레이터 표현식은 느리기 때문에 요구되지 않는 한 모든 계산이나 API 호출이 수행되지는 않습니다.이 경우 제너레이터 식을 사용하는 것이 더 효율적입니다.

목록 압축에서는 전체 컬렉션이 메모리에 로드됩니다.하지만 생성기 표현식, 일단 값이 반환되면next()콜은 이것으로 완료되어 메모리에 보존할 필요가 없어집니다.하나의 항목만 메모리에 로드됩니다.디스크 내의 대용량 파일을 반복하는 경우 파일이 너무 크면 메모리 문제가 발생할 수 있습니다.이 경우 제너레이터 식을 사용하는 것이 더 효율적입니다.

대부분의 답변이 놓친 것이 있습니다.목록 이해는 기본적으로 목록을 생성하여 스택에 추가합니다.목록 개체가 매우 큰 경우 스크립트 프로세스가 중지됩니다.이 경우 제너레이터 값은 메모리에 저장되지 않고 상태 저장 함수로 저장되므로 제너레이터가 더 선호됩니다.또한 생성 속도, 목록 이해 속도가 생성기 이해 속도보다 느림

요약하면, obj의 크기가 지나치게 크지 않을 때 목록 이해 사용, 그렇지 않을 경우 제너레이터 이해 사용

기능 프로그래밍을 위해 가능한 한 인덱스를 적게 사용하고 싶습니다.따라서 첫 번째 요소의 슬라이스를 가져온 후에도 요소를 계속 사용하려면 반복자 상태가 저장되므로 islice()를 선택하는 것이 좋습니다.

from itertools import islice

def slice_and_continue(sequence):
    ret = []
    seq_i = iter(sequence) #create an iterator from the list

    seq_slice = islice(seq_i,3) #take first 3 elements and print
    for x in seq_slice: print(x),

    for x in seq_i: print(x**2), #square the rest of the numbers

slice_and_continue([1,2,3,4,5])

출력: 1 2 3 16 25

내장된 Python 함수에 대한 참고 사항:

또는 의 단락 동작을 이용할 필요가 있는 경우는 제너레이터 식을 사용합니다.이러한 함수는 응답이 알려진 경우 반복을 중지하도록 설계되어 있지만 함수를 호출하려면 목록 이해가 모든 요소를 평가해야 합니다.(any그리고.all는, 리스트의 이해로부터 일람을 얻을 수 있는 경우에서도, 그 요소의 진실성을 체크하는 것을 정지합니다.다만, 이것은, 실제로 요소를 계산하는 작업에 비하면, 통상 사소한 것입니다).

제너레이터 표현식은 물론 사용할 수 있는 경우 메모리 효율이 더 높습니다.리스트의 수집은, 쇼트하지 않는 서킷을 사용하는 으로, 약간 고속화됩니다.min,max그리고.sum(타이밍:max이하에 나타냅니다.

$ python -m timeit "max(_ for _ in range(1))"
500000 loops, best of 5: 476 nsec per loop
$ python -m timeit "max([_ for _ in range(1)])"
500000 loops, best of 5: 425 nsec per loop
$ python -m timeit "max(_ for _ in range(100))"
50000 loops, best of 5: 4.42 usec per loop
$ python -m timeit "max([_ for _ in range(100)])"
100000 loops, best of 5: 3.79 usec per loop
$ python -m timeit "max(_ for _ in range(10000))"
500 loops, best of 5: 468 usec per loop
$ python -m timeit "max([_ for _ in range(10000)])"
500 loops, best of 5: 442 usec per loop

언급URL : https://stackoverflow.com/questions/47789/generator-expressions-vs-list-comprehensions

반응형