이터레이터와 제너레이터

PythonBeginner
지금 연습하기

소개

이 랩에서는 Python 의 내장 이터레이터 (Iterators), 제너레이터 (Generators), 그리고 제너레이터 표현식 (Generator Expressions) 에 대해 배우겠습니다. 이러한 구조를 사용하여 Python 에서 효율적이고 우아한 코드를 작성하는 방법을 살펴볼 것입니다.

성과

  • 이터레이터 (Iterator)
  • 제너레이터 (Generator)
  • 제너레이터 표현식 (Generator Expression)
이것은 가이드 실험입니다. 학습과 실습을 돕기 위한 단계별 지침을 제공합니다.각 단계를 완료하고 실무 경험을 쌓기 위해 지침을 주의 깊게 따르세요. 과거 데이터에 따르면, 이것은 중급 레벨의 실험이며 완료율은 64%입니다.학습자들로부터 100%의 긍정적인 리뷰율을 받았습니다.

이터레이터 (Iterator)

이터레이터는 반복 (루프) 될 수 있는 객체입니다. 한 번에 하나의 요소를 반환하는 객체입니다. Python 에서 이터레이터는 리스트, 튜플 또는 문자열과 같은 이터러블 (iterable) 객체로부터 생성됩니다.

새로운 Python 인터프리터를 엽니다.

python3

Python 에서 이터레이터를 생성하려면 객체에 __iter____next__ 두 가지 메서드를 구현해야 합니다.

__iter__는 이터레이터 객체 자체를 반환합니다. __next__ 메서드는 이터레이터에서 다음 값을 반환합니다. 반환할 항목이 더 이상 없으면 StopIteration을 발생시켜야 합니다.

다음은 숫자 리스트를 반복하는 간단한 이터레이터의 예입니다.

class MyIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        ## len() 은 리스트의 요소 수입니다.
        if self.index >= len(self.data):
            raise StopIteration
        result = self.data[self.index]
        self.index += 1
        return result

iterator = MyIterator([1, 2, 3, 4, 5])
for x in iterator:
    print(x)

출력:

1
2
3
4
5

이터레이터는 모든 요소를 한 번에 메모리에 로드하는 대신, 이터러블의 요소를 한 번에 하나씩 접근할 수 있게 해주기 때문에 유용합니다. 이는 메모리에 맞지 않는 대규모 데이터 세트를 사용할 때 특히 유용할 수 있습니다.

이터레이터는 Python 에서 지연 평가 (lazy evaluation) 를 구현하는 데에도 사용됩니다. 이는 이터레이터의 요소가 모든 요소를 미리 생성하는 대신 필요할 때만 생성됨을 의미합니다. 이는 불필요한 요소를 생성하고 저장하는 것을 피할 수 있으므로 더 효율적인 접근 방식이 될 수 있습니다.

이터레이터에서 한 번에 하나의 요소를 얻으려면 next() 함수를 사용할 수 있습니다. 이 함수는 이터레이터에서 다음 요소를 반환합니다. 더 이상 요소가 없으면 StopIteration 예외를 발생시킵니다.

iterator = MyIterator([1, 2, 3, 4, 5])
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))

## StopIteration
print(next(iterator))

출력:

1
2
3
4

## StopIteration

다음은 Python 에서 이터레이터의 몇 가지 일반적인 사용 사례입니다.

  1. 대규모 데이터 세트의 요소를 한 번에 하나씩 반복합니다.
  2. 대규모 데이터 세트의 지연 평가를 구현합니다.
  3. 클래스에서 사용자 정의 반복 로직을 구현합니다.
  4. 이터레이터는 Python 에서 강력한 도구이며 효율적이고 우아한 코드를 작성하는 데 사용할 수 있습니다.

제너레이터 (Generator)

제너레이터는 함수를 사용하여 생성되는 특수한 유형의 이터레이터입니다. 함수를 사용하여 이터레이터를 생성하는 간단한 방법입니다.

제너레이터 함수는 일반 함수처럼 정의되지만, 값을 반환하기 위해 return 키워드를 사용하는 대신 yield 키워드를 사용합니다. 제너레이터 함수가 호출되면 함수 본문이 즉시 실행되지 않습니다. 대신, 필요에 따라 함수 본문을 실행하는 데 사용할 수 있는 제너레이터 객체를 반환합니다.

제너레이터 함수는 본문 내 어디에서든 yield 문을 가질 수 있습니다. 제너레이터 함수가 호출되면 함수 본문이 즉시 실행되지 않습니다. 대신, 필요에 따라 함수 본문을 실행하는 데 사용할 수 있는 제너레이터 객체를 반환합니다.

다음은 숫자 리스트의 제곱을 생성하는 제너레이터 함수의 예입니다.

def my_generator(data):
    for x in data:
        yield x**2

for x in my_generator([1, 2, 3, 4, 5]):
    print(x)

출력:

1
4
9
16
25

제너레이터는 모든 요소를 미리 생성하는 대신 필요에 따라 요소를 생성할 수 있기 때문에 유용합니다. 이는 불필요한 요소를 생성하고 저장하는 것을 피할 수 있으므로 더 효율적인 접근 방식이 될 수 있습니다.

제너레이터는 Python 에서 지연 평가 (lazy evaluation) 를 구현하는 데에도 사용됩니다. 이는 제너레이터의 요소가 모든 요소를 미리 생성하는 대신 필요할 때만 생성됨을 의미합니다. 이는 불필요한 요소를 생성하고 저장하는 것을 피할 수 있으므로 더 효율적인 접근 방식이 될 수 있습니다.

다음은 Python 에서 제너레이터의 몇 가지 일반적인 사용 사례입니다.

  1. 모든 요소를 미리 생성하는 대신 필요에 따라 요소를 생성합니다.
  2. 대규모 데이터 세트의 지연 평가를 구현합니다.
  3. 함수에서 사용자 정의 반복 로직을 구현합니다.
  4. 제너레이터는 Python 에서 강력한 도구이며 효율적이고 우아한 코드를 작성하는 데 사용할 수 있습니다.

이터레이터와 제너레이터의 차이점

이터레이터와 제너레이터의 주요 차이점은 구현 방식입니다.

이터레이터는 __iter____next__ 두 가지 메서드를 구현하는 객체입니다. __iter__ 메서드는 이터레이터 객체 자체를 반환하고, __next__ 메서드는 이터레이터에서 다음 값을 반환합니다.

제너레이터는 값을 반환하기 위해 yield 키워드를 사용하는 함수입니다. 제너레이터 함수가 호출되면 함수 본문이 즉시 실행되지 않습니다. 대신, 필요에 따라 함수 본문을 실행하는 데 사용할 수 있는 제너레이터 객체를 반환합니다.

다음은 이터레이터와 제너레이터의 주요 차이점에 대한 요약입니다.

  1. 이터레이터는 __iter____next__ 메서드를 구현하는 객체입니다. 리스트, 튜플 또는 문자열과 같은 이터러블 객체로부터 생성됩니다.
  2. 제너레이터는 값을 반환하기 위해 yield 키워드를 사용하는 함수입니다. 제너레이터 함수를 호출하여 생성됩니다.
  3. 이터레이터는 클래스를 사용하여 구현할 수 있지만, 제너레이터는 함수를 사용하여 구현됩니다.
  4. 이터레이터는 한 번에 하나의 요소를 반환하는 반면, 제너레이터는 필요에 따라 요소를 생성하는 데 사용할 수 있는 제너레이터 객체를 반환합니다.
  5. 이터레이터는 이터러블 객체의 요소에 한 번에 하나씩 접근하는 데 사용되는 반면, 제너레이터는 필요에 따라 요소를 생성하는 데 사용됩니다.

전반적으로 이터레이터와 제너레이터는 모두 Python 에서 일련의 요소를 반복하는 데 유용한 도구입니다. 이를 통해 시퀀스의 요소에 한 번에 하나씩 접근하거나 생성할 수 있으며, 이는 모든 요소를 미리 생성하는 것보다 더 효율적일 수 있습니다.

고급 예제: 소수 생성기 (Prime Number Generator)

이 예제에서는 소수를 생성하는 제너레이터를 만들 것입니다.

먼저, 숫자가 소수이면 True를 반환하고 그렇지 않으면 False를 반환하는 도우미 함수 _is_prime을 정의해 보겠습니다.

def _is_prime(n):
    if n < 2:
        return False
    for i in range(2, int(n ** 0.5) + 1):
        if n % i == 0:
            return False
    return True

이제 제너레이터 함수 prime_numbers를 정의해 보겠습니다.

def prime_numbers(n):
    for i in range(2, n+1):
        if _is_prime(i):
            yield i

제너레이터를 테스트해 보겠습니다.

for prime in prime_numbers(20):
    print(prime)

출력:

2
3
5
7
11
13
17
19

제너레이터 표현식 (Generator Expression)

제너레이터 표현식은 리스트 컴프리헨션과 유사하지만, 리스트를 생성하는 대신 제너레이터 객체를 반환합니다.

제너레이터 표현식은 괄호 ()를 사용하여 정의되며, 하나 이상의 for 절을 포함할 수 있습니다. 필요에 따라 평가되며, 표현식의 요소를 필요에 따라 생성하는 데 사용할 수 있는 제너레이터 객체를 반환합니다.

다음은 숫자 리스트의 제곱을 생성하는 제너레이터 표현식의 예입니다.

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

출력:

1
4
9
16
25

제너레이터 표현식은 모든 요소를 미리 생성하는 대신 필요에 따라 요소를 생성할 수 있기 때문에 유용합니다. 이는 불필요한 요소를 생성하고 저장하는 것을 피할 수 있으므로 더 효율적인 접근 방식이 될 수 있습니다.

제너레이터 표현식은 Python 에서 지연 평가 (lazy evaluation) 를 구현하는 데에도 사용됩니다. 이는 제너레이터 표현식의 요소가 모든 요소를 미리 생성하는 대신 필요할 때만 생성됨을 의미합니다. 이는 불필요한 요소를 생성하고 저장하는 것을 피할 수 있으므로 더 효율적인 접근 방식이 될 수 있습니다.

다음은 Python 에서 제너레이터 표현식의 몇 가지 일반적인 사용 사례입니다.

  1. 모든 요소를 미리 생성하는 대신 필요에 따라 요소를 생성합니다.
  2. 대규모 데이터 세트의 지연 평가를 구현합니다.
  3. 간결하고 효율적인 코드를 작성합니다.

제너레이터 표현식은 Python 에서 강력한 도구이며 효율적이고 우아한 코드를 작성하는 데 사용할 수 있습니다.

제너레이터 표현식과 리스트 컴프리헨션 (Generator Expression & List Comprehension)

다음은 숫자 리스트의 제곱을 생성하는 리스트 컴프리헨션과 제너레이터 표현식의 예입니다.

## 리스트 컴프리헨션
squares = [x**2 for x in [1, 2, 3, 4, 5]]
print(squares)

## 제너레이터 표현식
squares_generator = (x**2 for x in [1, 2, 3, 4, 5])
for x in squares_generator:
    print(x)

출력:

[1, 4, 9, 16, 25]
1
4
9
16
25

리스트 컴프리헨션과 제너레이터 표현식 사이에는 몇 가지 유사점과 차이점이 있습니다.

유사점

  1. 리스트 컴프리헨션과 제너레이터 표현식 모두 요소 시퀀스를 생성하는 데 사용됩니다.
  2. 둘 다 동일한 구문을 사용하며, 하나 이상의 for 절과 요소를 생성하는 표현식을 사용합니다.

차이점

  1. 리스트 컴프리헨션은 리스트를 생성하는 반면, 제너레이터 표현식은 제너레이터 객체를 생성합니다.
  2. 리스트 컴프리헨션은 리스트의 모든 요소를 미리 생성하는 반면, 제너레이터 표현식은 필요에 따라 요소를 생성합니다.
  3. 리스트 컴프리헨션은 모든 요소를 리스트에 저장하므로 더 많은 메모리를 사용하고, 제너레이터 표현식은 필요에 따라 요소를 생성하므로 더 적은 메모리를 사용합니다.
  4. 리스트 컴프리헨션은 모든 요소를 미리 생성하므로 일반적으로 실행 속도가 빠르며, 제너레이터 표현식은 필요에 따라 요소를 생성하므로 일반적으로 실행 속도가 느립니다.

전반적으로 리스트 컴프리헨션과 제너레이터 표현식 모두 Python 에서 요소 시퀀스를 생성하는 데 유용한 도구입니다. 리스트 컴프리헨션은 일반적으로 더 빠르고 더 많은 메모리를 사용하며, 제너레이터 표현식은 일반적으로 더 느리고 더 적은 메모리를 사용합니다. 어떤 것을 사용할지는 애플리케이션의 특정 요구 사항에 따라 다릅니다.

요약

이 랩에서는 Python 의 내장 이터레이터 (Iterators), 제너레이터 (Generators) 및 제너레이터 표현식 (Generator Expressions) 에 대해 배웠습니다. 이러한 구조를 사용하여 Python 에서 효율적이고 우아한 코드를 작성하는 방법을 살펴보았습니다. 또한 제너레이터를 사용하여 소수 생성기를 구현하는 예시도 살펴보았습니다.