[python 기초] generators

EMMA·2022년 3월 7일
0

generator 함수는 return 값을 돌려주는 함수와 달리 yield(산출)의 개념이 들어가 있다. 해당 함수가 gereator 객체인지 확인하는 방법은 아래와 같다.

def generator_squares():
    for i in range(3):
        yield i ** 2

print(generator_squares)
> gen object=<generator object generator_squares at 0x10372d770>

그리고 dir(generator_squares())를 입력하면, __iter____next__ 둘 다 있는 것을 알 수 있다. 그래서 iterator와 달리 바로 __next__를 사용할 수 있다.
(iterator는 __iter__만 갖고 있으며, __iter__을 호출한 후에 __next__를 호출할 수 있다)

def generator_squares():
    for i in range(3):
        yield i ** 2

gen = generator_squares()
print(gen.__next__())	>0
print(gen.__next__())	>1
print(gen.__next__())	>4
print(gen.__next__())	>#StopIteration

generator 함수의 가장 큰 특징은, yield(산출)라는 개념이 있으며, return과 유사하나 yield는 변수의 마지막 값과 상태(next가 호출되기 전의 값과 상태)를 저장한다.

그래서, 일반 함수가 return 명령어를 만나면 값을 반환하고 함수 자체를 종료시키지만, generator 함수는 yield를 만나고 그 때까지 연산 완료된 값을 반환할 뿐 함수를 종료하지는 않는다.

def generator_send():
    received_value = 0 

    while True:
        received_value = yield
        print("received_value =", end=""), print(received_value)
        yield received_value * 2

gen = generator_send()
next(gen)
print(gen.send(2))
> received_value = 2 
> 4

위의 예시처럼, send()함수를 사용하면, generator 함수 실행 중에 yield를 통해 값을 전달할 수 있다.


generator expression은 lazy evaluation(실행 지연)을 위해 사용된다.
기본 문법은 list comprehensions와 비슷하나 [ ]가 아니라 ( )로 묶는다. 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


#comprehension list 
comprehension_list = [lazy_return(i) for i in L]
print(print_iter(comprehension_list))
> sleep 1s
> sleep 1s
> sleep 1s
> 1
> 2
> 3

#generator expression
generator_exp = (lazy_return(i) for i in L)
print(print_iter(generator_exp))
> sleep 1s
> 1
> sleep 1s
> 2
> sleep 3s
> 3

첫 번째는 list comprehension으로 표현한 것이고, 두 번째는 generator expression을 통해 lazy evaluation을 실행한 것이다.
generator expression은 여러 면에서 list comprehension과 다른 특징이 있는데, 크게 3가지가 있다.
(차이점 및 예시에 대한 자세한 내용은: 여기 클릭)

1) 문법
: list comprehension은 [ ]로, geneator expression은
( )으로 묶는다.

2) 사용 메모리 크기
: list comprehension은 생성되는 모든 요소를 list 안에 저장하지만, generator expression은 iterator만 생성하고 값은 필요한 시점에만 만들어서 반환한다. 그래서 사용하는 메모리 크기가 훨찍 작다.
이러하다 보니, list comprehension처럼 임의의 값을 출력할 수 없다.

lst = [n*2 for n in range(1000)]
gen = (n*2 for n in range(1000))

print(lst[4])
> 8

print(gen[4])
>#TypeError: 'generator' object is not subscriptable 

3) lazy evaluation
데이터 생성을 뒤로 미루기 때문에 lazy evaluation이라는 특징을 갖는 것이다.
위의 print_iter() 예시를 보면, list comprehensions는 return값을 반환하기 전에 comprehension_list에 적재된 "sleep 1s" 3번을 모두 출력하고나서야 print_iter를 실행해 return 값을 출력한다.
반면 generator expression은 최종 return값을 실제로 반환하기 전까지는(즉 next()가 호출되기 전까지는) 그 다음 loop로 넘어가지 않는다.
그래서 무한 iterable자료를 만들 수도 있다.

profile
예비 개발자의 기술 블로그 | explore, explore and explore

0개의 댓글