소개
이 랩에서는 Python 에서 제너레이터 함수를 식별하고 일반 함수와 제너레이터의 근본적인 차이점을 이해하는 방법을 배우게 됩니다. 이 지식은 특히 대용량 데이터 세트 또는 무한 시퀀스를 처리할 때 효율적이고 메모리 친화적인 코드를 작성하는 데 매우 중요합니다.
이 랩에서는 함수와 제너레이터를 구별하고, inspect.isgeneratorfunction을 사용하여 유형을 확인하고, yield 키워드의 존재 여부를 테스트하는 과정을 안내합니다. Python 스크립트를 생성하여 차이점을 설명하고, 함수가 완전한 결과를 반환하는 반면 제너레이터는 필요에 따라 실행을 일시 중지하고 재개하면서 값을 반복적으로 생성하는 방식을 관찰하게 됩니다.
함수와 제너레이터 구분하기
이 단계에서는 일반 Python 함수와 제너레이터의 주요 차이점을 배우게 됩니다. 특히 대용량 데이터 세트 또는 무한 시퀀스를 처리할 때 효율적이고 메모리 친화적인 코드를 작성하려면 이러한 구별을 이해하는 것이 중요합니다.
함수 (Functions):
함수는 특정 작업을 수행하는 코드 블록입니다. 함수가 호출되면 코드를 실행하고, 잠재적으로 계산을 수행하며 값을 반환합니다 (또는 명시적인 return 문이 없으면 None을 반환합니다). 함수의 상태는 호출 간에 유지되지 않습니다.
제너레이터 (Generators):
제너레이터는 return 대신 yield 키워드를 사용하는 특수한 유형의 함수입니다. 제너레이터가 호출되면 이터레이터 (iterator) 객체를 반환합니다. 이터레이터에서 값을 요청할 때마다 제너레이터는 yield 문을 만날 때까지 실행됩니다. 그런 다음 제너레이터는 일시 중지하고 상태를 저장한 후 값을 생성합니다. 다음에 값을 요청하면 제너레이터는 중단된 지점부터 다시 시작합니다.
예시를 통해 이를 설명해 보겠습니다. 먼저 VS Code 편집기를 사용하여 ~/project 디렉토리에 function_vs_generator.py라는 파일을 생성합니다.
## ~/project/function_vs_generator.py
## Regular function
def square_numbers_function(numbers):
result = []
for number in numbers:
result.append(number * number)
return result
## Generator function
def square_numbers_generator(numbers):
for number in numbers:
yield number * number
## Example usage
numbers = [1, 2, 3, 4, 5]
## Using the function
function_result = square_numbers_function(numbers)
print("Function Result:", function_result)
## Using the generator
generator_result = square_numbers_generator(numbers)
print("Generator Result:", list(generator_result)) ## Convert generator to list for printing
이제 Python 스크립트를 실행합니다.
python ~/project/function_vs_generator.py
다음과 같은 출력을 볼 수 있습니다.
Function Result: [1, 4, 9, 16, 25]
Generator Result: [1, 4, 9, 16, 25]
함수와 제너레이터 모두 동일한 결과를 생성합니다. 그러나 핵심적인 차이점은 이를 달성하는 방식에 있습니다. 함수는 모든 제곱을 계산하고 반환하기 전에 목록에 저장합니다. 반면에 제너레이터는 요청될 때마다 각 제곱을 한 번에 하나씩 생성합니다.
차이점을 더 자세히 설명하기 위해 스크립트를 수정하여 반환된 객체의 유형을 출력해 보겠습니다.
## ~/project/function_vs_generator.py
## Regular function
def square_numbers_function(numbers):
result = []
for number in numbers:
result.append(number * number)
return result
## Generator function
def square_numbers_generator(numbers):
for number in numbers:
yield number * number
## Example usage
numbers = [1, 2, 3, 4, 5]
## Using the function
function_result = square_numbers_function(numbers)
print("Function Result Type:", type(function_result))
## Using the generator
generator_result = square_numbers_generator(numbers)
print("Generator Result Type:", type(generator_result))
스크립트를 다시 실행합니다.
python ~/project/function_vs_generator.py
출력은 다음과 같습니다.
Function Result Type: <class 'list'>
Generator Result Type: <class 'generator'>
이것은 함수가 list를 반환하는 반면 제너레이터는 generator 객체를 반환함을 명확하게 보여줍니다. 제너레이터는 모든 값을 한 번에 메모리에 저장하지 않기 때문에 메모리 효율적입니다. 필요에 따라 값을 생성합니다.
inspect.isgeneratorfunction 으로 타입 확인
이전 단계에서 함수와 제너레이터의 기본적인 차이점을 배웠습니다. 이제 inspect.isgeneratorfunction() 메서드를 사용하여 함수가 제너레이터 함수인지 프로그래밍 방식으로 확인하는 방법을 살펴보겠습니다. 이는 함수의 구현 세부 사항을 알 수 없는 코드를 사용할 때 특히 유용합니다.
Python 의 inspect 모듈은 인트로스펙션 (introspection) 을 위한 도구를 제공합니다. 즉, 함수, 클래스, 모듈 등과 같은 객체의 내부 특성을 검사하는 것을 의미합니다. inspect.isgeneratorfunction() 메서드는 특정 객체가 제너레이터 함수인지 여부를 구체적으로 확인합니다.
~/project 디렉토리의 function_vs_generator.py 파일을 수정하여 이 검사를 포함해 보겠습니다. VS Code 에서 파일을 열고 다음 코드를 추가합니다.
## ~/project/function_vs_generator.py
import inspect
## Regular function
def square_numbers_function(numbers):
result = []
for number in numbers:
result.append(number * number)
return result
## Generator function
def square_numbers_generator(numbers):
for number in numbers:
yield number * number
## Check if it's a generator function
print("Is square_numbers_function a generator function?", inspect.isgeneratorfunction(square_numbers_function))
print("Is square_numbers_generator a generator function?", inspect.isgeneratorfunction(square_numbers_generator))
이 코드에서는 먼저 inspect 모듈을 가져옵니다. 그런 다음 inspect.isgeneratorfunction()을 사용하여 square_numbers_function과 square_numbers_generator를 모두 확인합니다.
이제 Python 스크립트를 실행합니다.
python ~/project/function_vs_generator.py
다음과 같은 출력을 볼 수 있습니다.
Is square_numbers_function a generator function? False
Is square_numbers_generator a generator function? True
이는 inspect.isgeneratorfunction()이 제너레이터 함수를 올바르게 식별함을 확인합니다.
이 메서드는 제너레이터인지 일반 함수인지에 따라 함수를 다르게 처리해야 할 때 매우 유용합니다. 예를 들어, 제너레이터의 결과를 반복하려는 경우도 있지만, 일반 함수를 단순히 호출할 수도 있습니다.
yield 키워드 사용 테스트
이 단계에서는 yield 키워드의 존재가 제너레이터 함수를 근본적으로 정의하는 방법에 중점을 둡니다. 함수는 하나 이상의 yield 문을 포함하는 경우에만 제너레이터로 간주됩니다. 이를 확인하기 위해 간단한 테스트를 만들어 보겠습니다.
VS Code 를 사용하여 ~/project 디렉토리에서 function_vs_generator.py 파일을 엽니다. yield를 사용하지 않는 함수를 추가하고 잠재적인 제너레이터로 취급될 때 어떻게 동작하는지 살펴보겠습니다.
## ~/project/function_vs_generator.py
import inspect
## Regular function
def square_numbers_function(numbers):
result = []
for number in numbers:
result.append(number * number)
return result
## Generator function
def square_numbers_generator(numbers):
for number in numbers:
yield number * number
## Function that returns a list, not a generator
def return_list(numbers):
return [x for x in numbers]
## Check if it's a generator function
print("Is square_numbers_function a generator function?", inspect.isgeneratorfunction(square_numbers_function))
print("Is square_numbers_generator a generator function?", inspect.isgeneratorfunction(square_numbers_generator))
print("Is return_list a generator function?", inspect.isgeneratorfunction(return_list))
이 업데이트된 코드에서는 리스트 컴프리헨션을 사용하여 단순히 리스트를 반환하는 함수 return_list를 추가했습니다. yield 키워드를 포함하지 않습니다. 그런 다음 inspect.isgeneratorfunction()을 사용하여 return_list가 제너레이터 함수인지 확인합니다.
이제 Python 스크립트를 실행합니다.
python ~/project/function_vs_generator.py
다음과 같은 출력을 볼 수 있습니다.
Is square_numbers_function a generator function? False
Is square_numbers_generator a generator function? True
Is return_list a generator function? False
예상대로 return_list는 yield 키워드를 사용하지 않기 때문에 제너레이터 함수로 식별되지 않습니다. 이는 Python 에서 제너레이터를 정의하는 데 yield 키워드가 필수적임을 보여줍니다. yield 키워드가 없으면 함수는 일반 함수처럼 동작하여 값을 반환하거나 (None) 호출 간에 상태를 유지하지 않습니다.
요약
이 랩에서는 일반 Python 함수와 제너레이터의 근본적인 차이점을 배웠습니다. 함수는 코드 블록을 실행하고 값을 반환하는 반면, 제너레이터는 yield 키워드를 사용하여 필요에 따라 값을 생성하는 이터레이터 (iterator) 객체를 반환하며, 호출 간에 일시 중지하고 상태를 저장합니다.
특히 대용량 데이터 세트를 처리할 때 제너레이터가 메모리 효율적인 이유를 살펴보았습니다. 함수처럼 모든 값을 메모리에 저장하는 대신 한 번에 하나씩 값을 생성하기 때문입니다. 이 랩에서는 함수와 제너레이터를 모두 사용하여 숫자를 제곱하는 실용적인 예제를 통해 이러한 차이점을 보여주었습니다.



