**제너레이터 표현식(Generator Expression)**은 리스트 컴프리헨션과 비슷하게 생겼지만, 동작 방식은 완전히 다른 아주 똑똑한 도구입니다.

쉽게 비유하자면, 리스트 컴프리헨션은 **“사탕 100개가 든 봉지를 한꺼번에 사는 것”**이고, 제너레이터 표현식은 **“필요할 때마다 사탕을 하나씩 나오는 기계”**를 갖는 것과 같습니다.


1. 문법적 차이

가장 큰 차이는 괄호의 모양입니다.

  • 리스트 컴프리헨션: [ 대괄호 ] 사용 → 결과값이 바로 리스트로 생성됨.

  • 제너레이터 표현식: ( 소괄호 ) 사용 → 제너레이터 객체가 생성됨.

Python

# 리스트 컴프리헨션
list_comp = [x * x for x in range(10)] 
# 결과: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] (메모리에 다 올라감)

# 제너레이터 표현식
gen_exp = (x * x for x in range(10))
# 결과: <generator object ...> (계산할 준비만 끝낸 상태)

2. 왜 제너레이터를 쓸까요? (핵심 장점)

① 메모리 효율성 (Lazy Evaluation)

만약 1억 개의 숫자를 다뤄야 한다면, 리스트는 1억 개의 데이터를 모두 메모리에 올려야 해서 컴퓨터가 멈출 수 있습니다. 하지만 제너레이터는 **“다음 데이터는 뭐야?”**라고 요청받을 때만 그 데이터를 계산해서 보여줍니다.

② 처리 속도 (Short-circuit)

예를 들어, “이 리스트에 짝수가 하나라도 있어?”라는 질문에 답할 때, 리스트는 전체를 다 검사해서 리스트를 만든 뒤 확인하지만, 제너레이터는 첫 번째 짝수를 찾는 순간 계산을 멈출 수 있습니다.


3. 언제, 어떻게 사용하나요?

주로 max(), min(), sum(), any(), all() 같은 집계 함수에 데이터를 던져줄 때 가장 많이 사용합니다.

Python

# 1. 합계 구하기
total = sum(x for x in range(1000000)) # 메모리 낭비 없이 합계만 계산

# 2. 조건에 맞는 데이터 확인
# 파일 리스트 중 .py 파일이 하나라도 있는지 확인
has_python_file = any(name.endswith('.py') for name in file_list)

주의할 점: 제너레이터는 ‘일회용’입니다. 한 번 끝까지 데이터를 꺼내 쓰면 다시 사용할 수 없으므로, 여러 번 반복해서 데이터를 써야 한다면 리스트로 만드는 것이 낫습니다.


4. 질문하신 코드 다시 보기

Python

max_len = max(len(row) for row in lst)

여기서 (len(row) for row in lst)는 소괄호가 생략된 제너레이터 표현식입니다. (함수의 유일한 인자로 들어갈 때는 소괄호 생략 가능)

  1. max 함수가 “야, 첫 번째 값 줘봐”라고 하면,

  2. 제너레이터가 lst[0]의 길이를 계산해서 max에게 줍니다.

  3. max가 “다음 값은?” 하면 그제야 lst[1]의 길이를 계산합니다.

  4. 이 과정을 반복하며 최댓값 하나만 딱 남기고 나머지는 메모리에서 잊어버립니다.

이 개념이 이해되셨나요? 아니면 제너레이터와 리스트 중 어떤 상황에 어떤 걸 쓰는 게 좋을지 실제 예시가 더 궁금하신가요?