(Python) 제너레이터

Kepler·2020년 4월 1일
0

Python

목록 보기
10/12

정의

제너레이터는 반복자(iterator)와 같은 루프의 작용을 컨트롤하기 위해 쓰여지는 특별한 함수 또는 루틴이다. 사실 모든 제너레이터는 iterator이다.
제너레이터는 배열이나 리스트를 리턴하는 함수와 비슷하며, 호출을 할 수 있는 parameter를 가지고 있고, 연속적인 값들을 만들어 낸다. 하지만 한번에 모든 값을 포함한 배열을 만들어서 리턴하는 대신에 yield 구문을 이용해 한 번 호출될 때마다 하나의 값만을 리턴하고, 이런 이유로 일반 반복자에 비해 아주 작은 메모리를 필요로 한다. (Wikipedia)

일반 함수와 제너레이터의 차이점

일반함수 : 실행된 후 모든 컨트롤을 호출자(caller)에게 리턴하며, 동시에 함수에 포함된 내부 함수나 로컬 변수가 메모리상에서 사라지게 된다.

제너레이터 : 실행되도 함수나 로컬변수가 메모리에서 사라지지 않고, 대기하고 있다가 next를 통하여 호출되면 이어서 실행한다. 따라서 매번 다시 시작하는 일반 함수와 비교하여, 메모리 리소스를 크게 절약할 수 있다.

제너레이터 함수 만들기

먼저 일반함수의 형태를 살펴보자. square_numbers(nums) 함수는 nums에 받은 배열의 요소를 for문을 돌면서 제곱하여 그 값을 리턴한다. 함수를 실행하는 순간에, 모든 값이 배열에 담겨 리턴된다.

# 일반함수
def square_numbers(nums):
    result = []
    for i in nums:
        result.append(i * i)
    return result

my_nums = square_numbers([1, 2, 3, 4, 5])

print my_nums
> [1, 4, 9, 16, 25]

위의 함수를 제너레이터로 작성하면 다음과 같다. 새로운 결과값을 담는 results배열은 불필요하며, return대신에 yield를 사용하여 각각의 요소를 제곱한다. print를 찍어 보았을때, 결과값이 아닌 generator object를 리턴하며, 각각의 요소의 제곱값을 불러오기 위해서 next를 사용한다.

# 제너레이터
def square_numbers(nums):
    for i in nums:
        yield i * i

my_nums = square_numbers([1, 2, 3, 4, 5])

print my_nums
> <generator object square_numbers at 0x1007c8f50>

print next(my_nums)
> 1
print next(my_nums)
> 4

더이상 리턴할 요소가 없는데 next를 호출한 경우, StopIteration 에러가 뜬다.

...
print next(my_nums)
> 1
print next(my_nums)
> 4
print next(my_nums)
> 9
print next(my_nums)
> 16
print next(my_nums)
> 25
print next(my_nums)
Traceback (most recent call last):
  File "generator.py", line 12, in 
    print next(my_nums)
StopIteration

for문을 사용한 호출

제너레이터는 일반적으로 for문을 통해서 호출하여 사용한다. 이렇게 호출하면 StopIteration을 만날일이 없다!

def square_numbers(nums):
    for i in nums:
        yield i * i

my_nums = square_numbers([1, 2, 3, 4, 5])

for num in my_nums:
    print num

같은 코드를 list comprehension 으로 작성하면 더욱 pythonic해진다.
[]이 아니라 ()를 사용한것을 주목하자. []를 사용할경우 my_nums는 리스트에 담겨서 출력된다.

리스트로 변형할 경우 보기는 편하지만, 정작 제너레이터를 쓰는 의미가 사라진다. 왜냐하면, 결국 함수를 호출할 때 결과값을 한꺼번에 메모리에 저장하는 꼴이 되기 때문이다.

my_nums = (x*x for x in [1, 2, 3, 4, 5])

print my_nums
> <generator object <genexpr> at 0x1007c8f50>

for num in my_nums:
    print num
>1
 4
 9
 16
 25

참고자료:

profile
🔰

0개의 댓글