[Python] 제네레이터 (generator)

cdwde·2021년 5월 15일
0

🎈 제네레이터란?

  • iterator를 생성해주는 함수

  • 함수 본인이 한 일을 기억하고 있다가 재호출 시 이어서 할 수 있는 새로운 함수

  • 함수 안에 yield 키워드 사용

  • 함수 안에서 yield를 사용하면 함수는 제네레이터가 됨

  • 제네레이터는 한 번 호출될 때마다 하나의 값만 전달(yield)


🎈 제네레이터 특징

  • 제네레이터 사용하면 일반 함수보다 훨씬 좋은 퍼포먼스 낼 수 있고 메모리 리소스도 절약 가능

  • 리스트는 데이터를 한 번에 메모리에 적재하기 때문에 메모리 부족현상이 일어나 프로그램이 갑자기 죽을 수 있지만,
    제네레이터는 데이터에 접근할 때마다 메모리에 적재하기 때문에 리스트보다 안정적이고 효율적

  • 리스트의 경우 list comprehension을 수행할 때 리스트의 모든 값을 먼저 수행하기 때문에 수행되는 연산이 오래 걸리거나 연산된 값에 접근하는 시간이 오래 걸리지만,
    제네레이터는 그 때 그 때 yield를 통해 값에 접근하기 때문에 수행시간이 긴 연산을 필요한 순간까지 늦출 수 있음


🎈 제네레이터 사용해보기

  • yield 키워드를 사용해 generator 만들기
  • yield가 호출되면 암시적으로 return이 호출되고 한번 더 실행되면 yield 다음 코드가 실행됨
  • 더 이상 next 요소가 없으면 StopIteration 예외 발생
#제네레이터 사용 X
def func(nums):
    result = []
    for i in nums:
        result.append(i*i)
    return result

f = func([1, 2, 3, 4, 5])
print(f)

#제네레이터 사용
def generator(nums):
    for i in nums:
        yield i*i

gen = generator([1, 2, 3, 4, 5])
print(gen)	#제네레이터는 자신이 리턴할 모든 값을 메모리에 저장하지 않기 때문에 리스트로 보이지 않음
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))	#에러 발생

#출력 결과
#[1, 4, 9, 16, 25]
#<generator object generator at 0x00E35568>
#1
#4
#9
#16
#25
#StopIteration

  • generator는 일반적으로 for 루프를 통해서 호출
    for 루프는 어디서 멈추는지 알기 때문에 StopIteration 예외 발생 X
def generator(nums):
    for i in nums:
        yield i*i

gen = generator([1, 2, 3, 4, 5])
for g in gen:
    print(g)

#출력 결과
#1
#4
#9
#16
#25

  • generator expression 사용해서 더 간단히 만들기
gen = (x*x for x in [1, 2, 3, 4, 5])

print(gen)
for g in gen:
    print(g)
    
#출력 결과
#<generator object <genexpr> at 0x00CD57D0>
#1
#4
#9
#16
#25

  • for 루프 사용하지 않고 한 번에 제네레이터 데이터를 보고 싶다면
    제네레이터를 리스트로 변환하기
    (❗ 한 번 리스트로 변형하면 제네레이터가 가지고 있던 장점 다 잃음)
gen = (x*x for x in [1, 2, 3, 4, 5])
print(list(gen))

#출력 결과
#[1, 4, 9, 16, 25]

  • 그 때 그 때 생성되는 무한한 순서있는 객체를 모델링할 수 있음
def infinite_generator():
    cnt = 0
    while True:
        cnt += 1
        yield cnt

gen = infinite_generator()
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))

#출력 결과
#1
#2
#3
#4

🎈 yield from

  • yield문은 한 번씩 값을 바깥으로 전달
  • for문 대신 yield from iterable로 값 전달 가능
def generator1():
    a = [1, 2, 3]
    for i in a:
        yield i

#yield from 사용
def generator2():
    a = [1, 2, 3]
    yield from a

gen = generator1()
print(list(gen))
gen = generator2()
print(list(gen))


#출력 결과
#[1, 2, 3]
#[1, 2, 3]

참고
https://wikidocs.net/16069
http://schoolofweb.net/blog/posts/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%A0%9C%EB%84%88%EB%A0%88%EC%9D%B4%ED%84%B0-generator/
https://ssungkang.tistory.com/entry/python%EC%A0%9C%EB%84%88%EB%A0%88%EC%9D%B4%ED%84%B0generator%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90
https://dailyheumsi.tistory.com/82

0개의 댓글