python에서 제너레이터(Generator)를 생성할 때 사용되는 키워드
제너레이터는 반복 가능한 객체(iterable)을 만들어 주는데, 값을 하나씩 생성해서 반환하고 그 상태를 기억해 함수의 실행을 일시 중단 할 수 있다. 이를 통해 메모리를 효율적으로 사용한다.
'yield' 를 사용한 함수는 호출될 때 제너레이터 객체를 반환한다.
(python에서 제너레이터(Generator) 는 리스트나 튜플처럼 반복(iterable)할 수 있는 객체의 한 종류이다. 하지만 일반적인 리스트나 튜플과 달리, 제너레이터는 값을 한 번에 하나씩 "필요할 때" 생성(lazy evaluation)한다는 특징이 있다. 즉, 모든 값을 미리 메모리에 저장하는 것이 아니라, 요청이 있을 때마다 값을 생성하여 반환한다. 이러한 동작 방식 덕분에 매우 큰 데이터셋을 다루거나 무한한 시퀀스를 생성해야 할 때 메모리를 효율적으로 사용할 수 있다. 이 객체는 실제로 함수 내의 코드를 실행하지 않는다. 대신에 값을 필요할 때마다 호출자에게 반환한다.
함수의 실행은 'yield'에서 일시 중단되고, 호출자가 다음 값을 요청하면 다시 시작된다.
예를 들자면 0에서 시작해 무한히 증가하는 제너레이터를 만드는 함수 코드이다.
def infinite_generator():
i = 0
while True:
yield i
i +=1
# 제너레이터 객체 생성
generator_obj = infinite_generator()
print(next(generator_obj)) #output 0
print(next(generator_obj)) #output 1
print(next(generator_obj)) $output 2
즉 위 코드에서 보듯이, yield i 는 현재 값을 반환하고 함수의 실행을 일시 중단한다.
그리고 next()
함수를 사용해 호출자는 제너레이터로부터 값을 요청하고, 함수의 실행이 중단된 지점에서 다시 시작하여 다음 값을 반환한다.
yield
를 포함한 함수는 자동으로 제너레이터가 된다.yield
된 값을 반환한다.case 1. generator를 사용해서 짝수를 생성하는 코드
def evenNum(n):
""" n개의 짝수를 생성하는 제너레이터 함수"""
i=1
while n:
yield 2*i # 현재 i에 대해 2*i 값을 생성 (yield : 제너레이터 함수 - 함수 내에서 사용되고, 호출할 때 값을 반환하지만 함수를 종료하지 않고 상태 유지)
i+=1 # 일반적인 함수는 return을 만나면 실행이 끝나지만 yield를 사용하면 함수가 일시 정지(paused) 되고 나중에 다시 실행할 수 있음
n-=1
it = evenNum(10)
even_list=[]
while True:
try :
even_list.append(next(it)) #제너레이터 값을 다 생성하면 StopIteration 예외 바생
except StopIteration:
break
print(even_list)
case2. 비슷한 기능을 하는 list comprehension 사용
def evenNum_list(n):
return [2*i for i in range(1, n+1)]
even_list = evenNum_list(10)
print(even_list)
그에 반해 python generator를 사용하면,
메모리절약(lazy Evaluation) : yield
를 사용해서 값을 하나씩 생성해서 반환.
한꺼번에 n개의 값을 저장하지 않아 메모리 사용량이 줄어듦. 메모리 사용량이 O(1) 수준으로 유지
연산 효율성 : next(it)을 호출할 때마다 O(1) 시간에 값을 생성해서 반환하므로, 모든 값을 한꺼번에 생성하는 리스트 방식보다 더 빠르게 동작
def read_large_file(file_path):
with open(file_path, 'r') as file:
for line in file:
yield line.strip()
# 파일에서 데이터를 읽어오는 제너레이터 객체 생성
log_reader = read_large_file('large_log_file.txt')
# 호출자에게 값을 하나씩 반환
for line in log_readers:
print(line)
예를 들어 'asyncio' 라이브러리를 사용해서 비동기적으로 웹 페이지를 가져온다고 가정한다면
import aiohttp
import asyncio
async def fetch_url(url):
async with aiohttp.ClinetSession() as session:
async with sessionn.get(url) as response:
return await response.text()
async def async_data_generator(urls):
for url in urls:
data = await fetch_url(url)
yield data
# 비동기 작업을 위한 루프
async def main():
urls = ['http://example.com', 'http://example.org', 'http://example.net']
async for data in async_data_generator(urls):
print(data)
# 이벤트 루프 실행
asyncio.run(main())
위에서 'fetch_url'은 비동기적으로 주어진 url에서 데이터를 가져오는 역할이다.
'asynco_data_generator' 함수는 여러 url에 대해 'fetch_url' 함수를 호출하고 가져온 데이터를 제러네이터를 통해 반환한다.
main 함수에서 'asynco_data_generator'를 비동기적으로 호출하고 가져온 데이터를 출력하고 있다.
위와 같이 'yield'와 비동기 작업을 결합하면 여러 비동기적인 이벤트나 작업을 효율적으로 처리할 수 있다.