파이썬 제너레이터 관련 주제

Beginner

This tutorial is from open-source community. Access the source code

소개

이 섹션에서는 제너레이터 표현식과 itertools 모듈을 포함하여 몇 가지 추가적인 제너레이터 관련 주제를 소개합니다.

제너레이터 표현식 (Generator Expressions)

리스트 컴프리헨션 (list comprehension) 의 제너레이터 버전입니다.

>>> a = [1,2,3,4]
>>> b = (2*x for x in a)
>>> b
<generator object at 0x58760>
>>> for i in b:
...   print(i, end=' ')
...
2 4 6 8
>>>

리스트 컴프리헨션과의 차이점:

  • 리스트를 구성하지 않습니다.
  • 유일한 용도는 반복 (iteration) 입니다.
  • 한 번 사용하면 재사용할 수 없습니다.

일반적인 구문:

(<expression> for i in s if <conditional>)

함수 인수로도 사용할 수 있습니다.

sum(x*x for x in a)

모든 이터러블 (iterable) 에 적용할 수 있습니다.

>>> a = [1,2,3,4]
>>> b = (x*x for x in a)
>>> c = (-x for x in b)
>>> for i in c:
...   print(i, end=' ')
...
-1 -4 -9 -16
>>>

제너레이터 표현식의 주요 사용 사례는 시퀀스에 대해 어떤 계산을 수행하지만 결과를 한 번만 사용하는 코드입니다. 예를 들어, 파일에서 모든 주석을 제거하는 경우입니다.

f = open('somefile.txt')
lines = (line for line in f if not line.startswith('#'))
for line in lines:
    ...
f.close()

제너레이터를 사용하면 코드가 더 빠르게 실행되고 메모리를 적게 사용합니다. 스트림에 적용되는 필터와 같습니다.

제너레이터의 장점 (Why Generators)

  • 많은 문제를 반복 (iteration) 의 관점에서 훨씬 더 명확하게 표현할 수 있습니다.
    • 항목 모음을 반복하고 어떤 종류의 연산 (검색, 대체, 수정 등) 을 수행합니다.
    • 처리 파이프라인 (processing pipelines) 은 광범위한 데이터 처리 문제에 적용될 수 있습니다.
  • 더 나은 메모리 효율성.
    • 필요할 때만 값을 생성합니다.
    • 거대한 리스트를 구성하는 것과 대조적입니다.
    • 스트리밍 데이터 (streaming data) 에 대해 작동할 수 있습니다.
  • 제너레이터는 코드 재사용을 장려합니다.
    • 반복을 사용하는 코드에서 반복을 분리합니다.
    • 흥미로운 반복 함수 도구 상자를 구축하고 mix-n-match 할 수 있습니다.

itertools 모듈

itertools는 이터레이터 (iterators) / 제너레이터 (generators) 를 돕도록 설계된 다양한 함수를 포함하는 라이브러리 모듈입니다.

itertools.chain(s1,s2)
itertools.count(n)
itertools.cycle(s)
itertools.dropwhile(predicate, s)
itertools.groupby(s)
itertools.ifilter(predicate, s)
itertools.imap(function, s1, ... sN)
itertools.repeat(s, n)
itertools.tee(s, ncopies)
itertools.izip(s1, ... , sN)

모든 함수는 데이터를 반복적으로 처리합니다. 다양한 종류의 반복 패턴을 구현합니다.

자세한 내용은 PyCon '08 의 Generator Tricks for Systems Programmers 튜토리얼을 참조하십시오.

이전 연습에서 로그 파일에 기록되는 줄을 따라가서 이를 일련의 행으로 구문 분석하는 코드를 작성했습니다. 이 연습은 이를 기반으로 계속 진행됩니다. stocksim.py가 여전히 실행 중인지 확인하십시오.

연습 문제 6.13: 제너레이터 표현식 (Generator Expressions)

제너레이터 표현식은 리스트 컴프리헨션 (list comprehension) 의 제너레이터 버전입니다. 예를 들어:

>>> nums = [1, 2, 3, 4, 5]
>>> squares = (x*x for x in nums)
>>> squares
<generator object <genexpr> at 0x109207e60>
>>> for n in squares:
...     print(n)
...
1
4
9
16
25

리스트 컴프리헨션과는 달리, 제너레이터 표현식은 한 번만 사용할 수 있습니다. 따라서 다른 for 루프를 시도하면 아무것도 얻을 수 없습니다:

>>> for n in squares:
...     print(n)
...
>>>

연습 문제 6.14: 함수 인자 (Function Arguments) 에서의 제너레이터 표현식

제너레이터 표현식은 때때로 함수 인자에 배치됩니다. 처음에는 약간 이상해 보일 수 있지만, 다음 실험을 시도해 보십시오:

>>> nums = [1,2,3,4,5]
>>> sum([x*x for x in nums])    ## A list comprehension
55
>>> sum(x*x for x in nums)      ## A generator expression
55
>>>

위의 예에서, 제너레이터를 사용하는 두 번째 버전은 큰 리스트를 조작하는 경우 메모리를 훨씬 적게 사용합니다.

portfolio.py 파일에서 리스트 컴프리헨션을 포함하는 몇 가지 계산을 수행했습니다. 이를 제너레이터 표현식으로 바꿔보십시오.

연습 문제 6.15: 코드 단순화

제너레이터 표현식은 종종 작은 제너레이터 함수를 대체하는 데 유용합니다. 예를 들어, 다음과 같은 함수를 작성하는 대신:

def filter_symbols(rows, names):
    for row in rows:
        if row['name'] in names:
            yield row

다음과 같이 작성할 수 있습니다:

rows = (row for row in rows if row['name'] in names)

ticker.py 프로그램을 수정하여 적절하게 제너레이터 표현식을 사용하십시오.

요약

축하합니다! 더 많은 제너레이터 (Generators) 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 실력을 향상시킬 수 있습니다.