Advanced Python - generator [3/7]

Seob·2020년 7월 23일
0

TIL

목록 보기
19/36


(그냥 현재 심경 🤡)

Generator? 🤷🏻‍♂️

generator는 간단하고도 강력한 iterator를 생성해주는 함수이다. generator는 일반 함수처럼 작성되지만 데이터를 반환시키고 싶을 때 yield문을 사용한다. generator는 모든 데이터값과 어떤 statement가 마지막으로 실행되었는지 기억하고 next()가 호출될 때마다 yield호출 후 현재의 상태에서 머물고 있던 마지막 부분에서 다시 시작한다. 다음의 예제가 generator를 생성하는게 얼마나 쉬운지 보여준다.

def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]
        
for char in reverse('gold'):
    print(char)
>>>
d
l
o
g

generator로 할 수 있는 어떤것이든 iterators로도 할 수 있다. generator가 가지는 장점은 __iter__()__next__()메소드가 자동으로 생성된다는 점이다.

print(reverse('data'))
print(dir(reverse('data')))
>>>
<generator object reverse at 0x7fe1a7175190>
['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']

그리고 또 하나의 주요 기능은 지역 변수들과 실행 상태가 호출 간에 자동으로 보관된다는 점이다. 이것은 self.indexself.data와 같은 인스턴스 변수를 사용하는 접근법에 비교해 함수를 쓰기 쉽고 명료하게 만든다.

자동 메소드 생성과 프로그램 상태의 저장에 더해, 제너레이터가 종료할 때 자동으로 StopIteration에러를 띄운다. 조합하면, 이 기능들이 일반 함수를 작성하는 것만큼 이터레이터를 만들기 쉽게 만든다.

Generator Expressions

간단한 제너레이터는 리스트 컴프리헨션과 비슷하지만, [대괄호]대신, (괄호)를 사용하는 문법을 사용한 표현식으로 간결하게 코딩이 가능하다. 이 표현식들을 둘러싸는 함수가 제너레이터를 즉시 사용하는 상황을 위해 설계되었다. 제너레이터 표현식은 완전한 제너레이터 정의보다 간결하지만, 융통성은 떨어지고, 비슷한 리스트 컴프리헨션보다 메모리를 덜 쓰는 경향이 있다.

>>> sum(i*i for i in range(10))                 # sum of squares
285

>>> xvec = [10, 20, 30]
>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec))         # dot product
260

>>> unique_words = set(word for line in page  for word in line.split())

>>> valedictorian = max((student.gpa, student.name) for student in graduates)

>>> data = 'golf'
>>> list(data[i] for i in range(len(data)-1, -1, -1))
['f', 'l', 'o', 'g']

제너레이터 표현식은 Lazy evaluation을 위해서 사용될 수 있다. Lazy evaluation이 무엇인지는 다음 예제를 통해 알아보자.

import time

L = [1, 2, 3]


def print_iter(iter):
    for element in iter:
        print(element)


def lazy_return(num):
    print("sleep 1s")
    time.sleep(1)
    return num


print("comprehension_list=")
comprehension_list = [lazy_return(i) for i in L]
print_iter(comprehension_list)

print("generator_exp=")
generator_exp = (lazy_return(i) for i in L)
print_iter(generator_exp)

>>>

comprehension_list=
sleep 1s
sleep 1s
sleep 1s
1
2
3
generator_exp=
sleep 1s
1
sleep 1s
2
sleep 1s
3

위의 결과에서 볼 수 있듯이, lazy evaluation은 평가를 늦추고 필요할때 값을 계산한다.
comprehension_list에서는 미리 객체들을 모두 평가되어진 상태로 갖고 있다가 한번에 출력하였고, generator_exp에서는 요청이 들어올때만 평가를 하기때문에 정보가 많을수록 메모리상에 많은 이득을 가져다준다.

profile
Hello, world!

0개의 댓글