Skip to content

Instantly share code, notes, and snippets.

@nattybear
Last active November 16, 2020 22:56
Show Gist options
  • Save nattybear/70dc1158f92acf490e1a5daa502f64e6 to your computer and use it in GitHub Desktop.
Save nattybear/70dc1158f92acf490e1a5daa502f64e6 to your computer and use it in GitHub Desktop.
파이썬 제네레이터

이 글은 2019년 6월에 제가 어떤 네이버 카페에 적었던 글을 옮겨 적은 것입니다.

너무 큰 리스트

파이썬으로 코딩을 하다보면 리스트를 자주 사용합니다.

xs = [1, 2, 3]

리스트의 길이가 크지 않다면 상관 없지만 리스트 길이가 너무 클 때는 문제가 될 수 있습니다. 메모리를 많이 차지할 수도 있고 계산하는데 시간이 오래걸릴 수도 있습니다.

그렇다면 리스트의 내용을 처음부터 끝까지 다 다루는 것이 아니라 진짜 필요할때 처음부터 하나씩만 꺼내서 처리한다면 어떨까요?

이게 가능하다면 리스트의 길이가 크다고 해서 걱정하지 않아도 됩니다.

게다가 길이가 큰 리스트 뿐만 아니라 길이가 무한대인 리스트도 다룰 수 있게 됩니다!

키워드 yield

파이썬에는 yield라는 키워드가 있습니다.

return과 기능이 비슷합니다. 둘 다 함수 안에서 쓰이고 값을 돌려줍니다.

return은 리턴하는 순간 함수가 종료되고 함수 안에서 사용했던 지역 변수들은 버립니다.

yield는 사용하는 순간 함수가 종료되고 값을 돌려주는 것은 같지만 함수 안에서 사용했던 지역 변수들을 버리지 않고 기억해둡니다.

아래 코드는 yield를 이용해서 무한 리스트를 만드는 코드입니다.

def infinite(n):
  while True:
    yield n
    n += 1

함수 infinite는 인자 하나를 받습니다.

while True를 이용해서 무한 루프를 돕니다. 무한 루프이긴 하지만 다음 줄에 있는 yield를 만나는 순간 인자로 받았던 n을 리턴하고 함수가 끝납니다.

그러나 여기에서는 return이 아니라 yield를 사용했기 때문에 함수가 정말로 완전히 종료된 것은 아니고 종료되기 전의 상태는 저장되어 있습니다.

아래 코드와 같이 next 함수를 이용하면 무한 리스트의 원소를 앞에서부터 하나씩만 꺼낼 수 있습니다.

def infinite(n):
  while True:
    yield n
    n += 1

xs = infinite(2)  # [2, 3, 4, ...]

next(xs)  # 2
next(xs)  # 3

첫번째로 무한 리스트에 next 함수를 사용하면 첫번째 숫자인 2가 나옵니다.

두번째로 next 함수를 사용하면 infinite 함수가 바로 전에 yield를 사용하기 전 상태를 기억하고 있다가 이어서 실행합니다.

여기에서는 n1을 더해서 23이 되고

무한 루프 때문에 코드는 다시 yield를 만나게 되고 이제서야 3을 리턴합니다. 이때 리턴하긴 하지만 지금까지 상태를 버리진 않고 계속 기억합니다.

이런 식으로 yield 키워드를 이용하면 무한 리스트를 만들 수 있습니다.

제네레이터

파이썬에서는 이러한 형태의 리스트를 제네레이터 Generator 라고 부릅니다.

프로그래밍 언어에서는 어려운 말로 게으른 평가 Lazy Evaluation 라고 하기도 합니다.

하스켈 같은 함수형 언어에서는 이 게으른 평가가 기본으로 설정되어 있습니다.

2020년

여기서부터는 위 글을 쓴지 1년 정도 지난 시점에서 쓰는 것입니다.

당시에는 제가 yield라는 키워드를 겉으로 동작하는 모습만 보고 이해한 것을 적었습니다.

그런데 지금 다시 생각해보면 파이썬이 yield를 내부에서 어떻게 구현했는지는 잘 모르겠습니다.

통상 함수의 호출과 반환은 스택 메모리로 구현되는데 그렇다면 제네레이터는 스택 영역이 아니라 힙 영역에 생성되는 것인지 궁금하긴 하네요.

대문 링크

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment