[Python] Generators

미남잉·6일 전
0

유튜브 강의와 GPT로 정리하였습니다.

출처: 유튜브 - Generators - Advanced Python Tutorial #3

📌 1. Generator란?

✅ 일반 함수와의 차이점

일반적인 함수는 호출되면 모든 코드를 한 번에 실행하고 끝난다.
하지만 Generator 함수는 yield를 사용하여 실행을 중간에 멈추고, 다시 호출하면 멈춘 지점부터 실행을 이어갈 수 있음.

✅ 장점

  • 메모리 효율적: 한 번에 하나의 값만 메모리에 올려서 대용량 데이터 처리에 유리.
  • 이터레이터처럼 사용 가능: next()를 호출하여 한 번에 하나의 값을 가져올 수 있음.
  • 무한 시퀀스 처리 가능: 리스트와 달리 무한 반복이 가능하여 CPU와 메모리 낭비 방지.

📌 예제: 간단한 Generator

def my_generator():
    print("Start")
    yield 1
    print("Step 2")
    yield 2
    print("Step 3")
    yield 3
    print("End")

gen = my_generator()  # Generator 객체 생성

print(next(gen))  # 첫 번째 yield 실행
print(next(gen))  # 두 번째 yield 실행
print(next(gen))  # 세 번째 yield 실행
print(next(gen))

📌 실행 과정

Start
1
Step 2
2
Step 3
3
End
Traceback (most recent call last):
  ...
StopIteration

🔹 동작 설명

  • next(gen) 호출 → "Start" 출력 후 yield 1 실행 → 1 반환 후 멈춤.
  • next(gen) 호출 → "Step 2" 출력 후 yield 2 실행 → 2 반환 후 멈춤.
  • next(gen) 호출 → "Step 3" 출력 후 yield 3 실행 → 3 반환 후 멈춤.
  • next(gen) 호출 → "End" 출력 후 더 이상 yield가 없으므로 StopIteration 예외 발생.

📌 3. next() 함수

✅ next()는 Generator를 한 단계 진행하여 yield 값을 반환하는 역할.
✅ Generator에 더 이상 yield 값이 없으면 StopIteration 예외가 발생.

📌 예제: next() 사용

def simple_gen():
    yield "A"
    yield "B"
    yield "C"

gen = simple_gen()

print(next(gen))  # A
print(next(gen))  # B
print(next(gen))  # C
print(next(gen))  # StopIteration 발생

📌 4. Generator의 장점

✅ 메모리 효율적

  • 한 번에 모든 데이터를 생성하지 않고 필요할 때마다 값을 반환.
  • 대량의 데이터를 처리할 때 리스트보다 훨씬 적은 메모리 사용.

✅ 무한 시퀀스 처리 가능

  • while True를 이용해 무한 루프를 생성 가능.
  • 리스트는 크기가 제한되지만, Generator는 필요한 값만 계산해서 반환하므로 효율적.

📌 예제: 무한 숫자 Generator

def infinite_numbers():
    num = 1
    while True:
        yield num
        num += 1  # 계속 증가

gen = infinite_numbers()

print(next(gen))  # 1
print(next(gen))  # 2
print(next(gen))  # 3

=> 무한히 실행 가능

💡 리스트(range())를 사용하면 메모리가 한계에 도달하지만, Generator는 메모리 낭비 없이 값을 하나씩 생성 가능.

📌 5. Generator 표현식 (Generator Comprehension)

Python의 리스트 컴프리헨션(List Comprehension) 과 유사한 방식으로 Generator를 만들 수도 있음.

📌 리스트 vs Generator 표현식

# 리스트 컴프리헨션 (메모리를 차지함)
lst = [x * 2 for x in range(10)]
print(lst)  # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# Generator 표현식 (메모리 효율적)
gen = (x * 2 for x in range(10))
print(next(gen))  # 0
print(next(gen))  # 2
print(next(gen))  # 4

✅ 리스트 컴프리헨션은 모든 값을 한 번에 메모리에 저장하지만,
✅ Generator 표현식은 필요할 때만 값을 생성하여 메모리 사용량을 절약함.

📌 6. yield from 키워드 (서브 제너레이터)

여러 개의 Generator를 연결할 때 yield from을 사용하면 더 간결하게 코드 작성 가능.

📌 예제: yield from을 사용한 Generator

def sub_generator():
    yield 1
    yield 2
    yield 3

def main_generator():
    yield from sub_generator()  # sub_generator의 모든 값을 yield
    yield 4
    yield 5

gen = main_generator()

for value in gen:
    print(value)

📌 실행 결과

1
2
3
4
5

✅ yield from을 사용하면 중첩된 Generator 값을 간단하게 반환 가능
✅ for 루프에서 직접 yield를 호출하지 않아도 모든 값을 자동으로 가져옴

📌 7. Generator 실전 예제

📌 예제: 대용량 파일 한 줄씩 읽기

💡 일반적으로 readlines()를 사용하면 전체 파일을 한 번에 읽어 메모리를 많이 차지하지만, Generator를 사용하면 한 줄씩 읽을 수 있어 메모리 절약 가능.

def read_large_file(filename):
    with open(filename, "r") as file:
        for line in file:
            yield line.strip()  # 한 줄씩 반환

for line in read_large_file("large_data.txt"):
    print(line)  # 한 줄씩 출력

✅ 파일 크기에 관계없이 한 줄씩 처리하여 메모리 낭비를 방지.
✅ yield를 사용하여 필요할 때마다 줄을 읽어오기 때문에 메모리 사용량이 일정.

🔹 결론

개념설명
yieldGenerator에서 상태를 유지하면서 값을 반환
next()Generator의 다음 yield까지 실행
yield from다른 Generator 또는 Iterable의 모든 값을 가져오기
Generator 표현식()를 사용하여 메모리를 절약하면서 값 생성

✅ Generator는 리스트보다 메모리 효율적이며 필요할 때만 값을 생성
next()를 사용해 값을 하나씩 생성하면서 사용할 수 있음
yield from을 사용하면 중첩된 Generator를 간결하게 처리 가능
✅ 무한 루프(while True)나 대용량 데이터 처리에 최적화된 방식

profile
Computer Vision Engineer

0개의 댓글

관련 채용 정보