TIL # 28 Python-generator

강경훈·2020년 8월 24일
0
post-thumbnail

TIL #27에 iterator에 대한 글을 올렸다. 이런 iterator을 만들어 사용 할 수 있게 해주는 것을 generator 라고 한다.

1. generator

  • interator을 만들어 주는 기능
  • 값을 미리 전부 저장하지 않음(lazy evaluation)
  • 값을 미리 저장하지 않기 때문에 메모리 절약
  • 해당 generator의 값을 다 사용 하면 더 이상 사용 불가능

2. geneator function

  • 함수를 만드는 방법은 동일
  • return 대신 yield로 반환
  • yield를 산출이라고 하나, 양보하다의 이미가 더 적절
  • 함수 내에서 yield를 만나게 되면 함수의 진행을 멈추고 다시 원래 코드 실행
  • 원래 코드에서 next()을 통해 다시 함수를 진행할 수 있음
  • yield을 통해 함수를 정시하고 실행하고, 정지하고 실행 하고를 반복 할 수 있음
  • next 대신 for문과도 같이 많이 사용
  • 예)
def generator_squares():
    for i in range(3):
        yield i ** 2

gen = generator_squares()
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
> 출력 결과
0
1
4
Traceback (most recent call last):
  File "/Users/apple/Documents/Python/run.py", line 86, in <module>
    print(next(gen))
StopIteration
- 함수 내에 yield을 사용했으므로 generator function
- 처음 geneator_squares()을 호출 할 때는 함수 실행 X
- next을 통해서 함수를 실행하고, yield을 통해서 정지
- next를 계속 하다가 마지막 iternation이 끝나게 되면 StopIternation 예외 발생

3. send method

  • .send(값)을 이용하여 함수 내에 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)) # yield에 2를 전송 후 결과 출력
next(gen)
print(gen.send(3)) # yield에 3을 전송 후 결과 출력
> 출력 결과
received_value = 2
4
received_value = 3
6
- gen에 generator_send() 저장
- next을 통해 함수를 실행 후, receuved_value = yield에서 yield을 만나 함수 정지
- .send()을 통해 yield에 값을 보내고 함수 재실행

4. generator expression

  • list comprehension과 비슷하게 만들지만, list가 아닌 바로 iterator을 만든다.
  • list comprehension에서는 대괄호([])을 사용 했다면, generator expression은 소괄호(())을 사용한다.
def generate_square_from_list():
    result = ( x*x for x in L ) # generator expression
    print(result)
    return result #iterator을 return

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

print_iter(generate_square_from_list())
#ptint_iter는 매개변수로 iterator을 받음
- result = (x*x for x in L) 을 보면 list conprehension과 모양이 비슷
- result는 list가 아닌 iterator
- result = iter([x*x for x in L]) 과 같은 효과

5. list comprehension VS generator expression (lazy evaluation)

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

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

L = [ 1,2,3]

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이다.
  • lazy evaluation이란 값이 필요한 시점에 되었을 때 값을 만드는 방식으로, 데이터 생성을 뒤로 미루는 방식이다.
  • iterator는 lazy evaluation 방식으로 값을 만들기 때문에 메모리 절략에 용이하다.
  • list는 모든 값들을 저장하기 때문에 예제에서 함수가 L의 크기만큼 돌고 난 뒤 list에 저장되 값을 출력하였다.
  • generator는 값을 미리 저장하지 않기 때문에 값이 필요한 시점에서 함수가 실행되고 값을 출력 하였다.
profile
방랑하는 개발자

0개의 댓글