← 파이썬 강의 목록으로
⚙️
중급 (Intermediate)
list/dict/set comp · yield · 메모리 효율

2주차 — 컴프리헨션과 제너레이터

리스트·딕셔너리·집합 컴프리헨션으로 한 줄에 변환·필터·매핑을 처리하고, yield로 만드는 제너레이터로 메모리 효율적인 코드를 짭니다.

comprehensiongeneratoryield
소요 시간
2시간
난이도
📊 중급
선수 조건
🎯 1주차 (함수 심화)
결과물
한 줄 컴프리헨션 + 큰 데이터에 강한 제너레이터

이 강의에서 배우는 것

  • 1리스트·딕셔너리·집합 컴프리헨션을 작성한다
  • 2제너레이터 함수와 표현식을 이해한다
  • 3메모리 효율적인 코드를 짠다

1. 리스트 컴프리헨션

[표현식 for 변수 in 시퀀스 if 조건] 한 줄로 리스트 생성.

python
# 기존
squares = []
for x in range(10):
    squares.append(x * x)

# 컴프리헨션
squares = [x * x for x in range(10)]

조건 추가:

python
even_sq = [x * x for x in range(10) if x % 2 == 0]
# [0, 4, 16, 36, 64]

중첩:

python
pairs = [(i, j) for i in range(3) for j in range(3) if i != j]

2. 딕셔너리·집합 컴프리헨션

python
words = ["apple", "banana", "cherry"]

# 딕셔너리: {키: 값 for ...}
length_map = {w: len(w) for w in words}
# {'apple': 5, 'banana': 6, 'cherry': 6}

# 집합: {표현식 for ...}
unique_lens = {len(w) for w in words}
# {5, 6}

3. 제너레이터 함수

return 대신 yield. 결과를 한 번에 만들지 않고, 필요할 때마다 하나씩.

python
def fib(n):
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

for x in fib(10):
    print(x, end=" ")    # 0 1 1 2 3 5 8 13 21 34

리스트로 한 번에 보고 싶으면 list(fib(10)).

4. 제너레이터 표현식

대괄호 대신 소괄호 → 메모리 절약.

python
# 리스트 컴프리헨션 (메모리 다 잡아먹음)
total = sum([x * x for x in range(1_000_000)])

# 제너레이터 표현식 (한 번에 하나씩)
total = sum(x * x for x in range(1_000_000))

5. 언제 어느 것?

상황도구
작은 데이터 + 인덱스 필요리스트 컴프리헨션
큰 데이터 + 한 번 순회제너레이터
키-값 매핑딕셔너리 컴프리헨션
중복 제거집합 컴프리헨션

6. 자주 하는 실수

  1. 컴프리헨션이 너무 길고 복잡 — 가독성 떨어지면 그냥 for 루프
  2. 제너레이터를 두 번 순회 — 한 번 다 돌면 빈 상태. 다시 호출 필요.
  3. yield 가 함수 어디에든 있으면 함수 전체가 제너레이터 — return 으로 일반 값 못 돌려줌

7. FAQ

Q1. 컴프리헨션이 for문보다 항상 빠른가요?

보통 약간 빠름. 가장 큰 차이는 가독성과 의도 표현.

Q2. 제너레이터의 길이를 알 수 있나요?

안 됨. 길이가 필요하면 list로 변환 (메모리 사용).

Q3. yield 가 여러 개여도 되나요?

네. 호출할 때마다 다음 yield 까지 실행됨.

💻 예제 (examples)

실제로 실행해 결과를 확인할 수 있는 예제 코드입니다.

01_list_comp.py리스트 컴프리헨션
CODE
squares = [x * x for x in range(10)]
print(squares)

even_sq = [x * x for x in range(10) if x % 2 == 0]
print(even_sq)

# 중첩
pairs = [(i, j) for i in range(3) for j in range(3) if i != j]
print(pairs)
▶ 실행 결과
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[0, 4, 16, 36, 64]
[(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
02_dict_set_comp.py딕셔너리·집합 컴프리헨션
CODE
words = ["apple", "banana", "cherry"]

length_map = {w: len(w) for w in words}
print(length_map)

unique_lens = {len(w) for w in words}
print(unique_lens)
▶ 실행 결과
{'apple': 5, 'banana': 6, 'cherry': 6}
{5, 6}
03_generator.py제너레이터 함수 (피보나치)
CODE
def fib(n):
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

for x in fib(10):
    print(x, end=" ")
print()

# 리스트로
print(list(fib(10)))
▶ 실행 결과
0 1 1 2 3 5 8 13 21 34
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
04_generator_expr.py제너레이터 표현식 (메모리 비교)
CODE
# 리스트는 메모리에 다 만들어둠
total1 = sum([x * x for x in range(1_000_000)])

# 제너레이터는 한 번에 하나씩
total2 = sum(x * x for x in range(1_000_000))

print(total1 == total2, total1)
▶ 실행 결과
True 333332833333500000

📝 과제 (exercises)

직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.

과제 1

1~100 소수 (컴프리헨션)

목표: 컴프리헨션으로 1~100 사이 소수만 추출.

요구사항
  • is_prime 함수 정의
  • 리스트 컴프리헨션으로 필터링
입출력 예시
[2, 3, 5, 7, 11, 13, ..., 97]
정답 코드 펼치기 / 접기
SOLUTION
def is_prime(n):
    if n < 2: return False
    return all(n % i for i in range(2, int(n**0.5) + 1))

primes = [n for n in range(1, 101) if is_prime(n)]
print(primes)
▶ 실행 결과
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
과제 2

단어 길이 매핑

목표: 단어 리스트 → {단어: 길이} 딕셔너리 (컴프리헨션).

요구사항
  • dict comprehension 사용
입출력 예시
{'apple': 5, 'pear': 4, 'kiwi': 4, 'banana': 6}
정답 코드 펼치기 / 접기
SOLUTION
words = ["apple", "pear", "kiwi", "banana"]
length_map = {w: len(w) for w in words}
print(length_map)
▶ 실행 결과
{'apple': 5, 'pear': 4, 'kiwi': 4, 'banana': 6}
과제 3

큰 파일 라인 단위 제너레이터

목표: 파일을 라인 단위로 읽고 길이만 합계 (메모리 효율).

요구사항
  • 파일을 한 줄씩 yield 하는 제너레이터 함수
  • sum 으로 길이 합계
💡 힌트

with open(path) as f: for line in f: yield line

입출력 예시
라인 수: 3, 총 글자 수: 35
정답 코드 펼치기 / 접기
SOLUTION
def lines(path):
    with open(path, encoding="utf-8") as f:
        for line in f:
            yield line.rstrip("\n")

# 가상 파일 (실제로는 path 받음)
sample = ["첫 번째 줄입니다", "두 번째 줄", "세 번째 줄입니다 끝"]

# 데모 (제너레이터 흉내)
def lines_from_list(lst):
    for x in lst:
        yield x

count = 0
total = 0
for line in lines_from_list(sample):
    count += 1
    total += len(line)
print(f"라인 수: {count}, 총 글자 수: {total}")
▶ 실행 결과
라인 수: 3, 총 글자 수: 26
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗