문제

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 로그 파일이 있고 "ENTRY"라는 단어로 시작하는 모든 줄의 내용과 길이를 원한다고 가정해 보겠습니다.

따라서 목록 이해력을 작성하여 시작해 보십시오.

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)

아직 아무것도 읽혀지지 않았지만 이제 우리가 원하는 대로 데이터에 작용할 두 개의 생성기를 지정했습니다.

필터링된 행을 다른 파일에 작성해 보겠습니다.

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

지금 입력 파일을 읽습니다.우리의 for 루프가 계속해서 추가 라인을 요청하면 long_entries 발전기 수요 라인 entry_lines 생성기, 길이가 80자를 초과하는 항목만 반환합니다.그리고 차례로, 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

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

나는 Hadoop Mincemeat 모듈.나는 이것이 다음 사항에 주목해야 할 좋은 예라고 생각합니다.

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은 Generators의 모든 장점을 활용한 훌륭한 예입니다.

때로는 당신은 기능 itertools, 동일한 생성기에 대해 독립적으로 사용할 수 있는 여러 반복자를 반환합니다.

둘 다의 장점을 얻으려면 [(exp for x in iter)]을 사용하는 것이 어떻습니까?생성기 이해 및 목록 방법의 성능

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top