13. 컴프리헨션
- 리스트 컴프리헨션으로 반복+생성을 한 줄에 쓴다.
- 조건 필터와 변환을 컴프리헨션에 녹인다.
- 딕셔너리·세트 컴프리헨션을 활용한다.
zip·enumerate와 제너레이터 표현식을 안다.
먼저: 컴프리헨션이 뭔가요?
초급편 8장에서 "리스트 만들기" 패턴을 봤습니다. 빈 리스트를 만들고 반복하며 append하는 방식이죠.
squares = [] for n in range(1, 6): squares.append(n ** 2) print(squares) # [1, 4, 9, 16, 25]
이 패턴은 너무 흔해서 Python은 이를 한 줄로 쓰는 문법을 제공합니다. 바로 컴프리헨션(comprehension)입니다. "이해·내포"라는 뜻인데, 수학의 집합 표기 { n² | n ∈ 1..5 }를 코드로 옮긴 것이라 생각하면 됩니다.
squares = [n ** 2 for n in range(1, 6)] print(squares) # [1, 4, 9, 16, 25] ← 위와 똑같은 결과, 한 줄!
위 두 코드는 완전히 같은 일을 합니다. 컴프리헨션이 더 짧고, 익숙해지면 더 읽기 좋습니다.
flowchart LR
Src["range(1,6)<br/>1,2,3,4,5"]:::data --> Map["각 n에<br/>n**2 적용"]:::proc
Map --> Out["[1,4,9,16,25]"]:::result
classDef data fill:#a8dadc,stroke:#457b9d,color:#1d3557
classDef proc fill:#7fd8d8,stroke:#2a9d8f,color:#14532d
classDef result fill:#b8e6c1,stroke:#34a853,color:#14532d
[식 for 변수 in 반복가능]이다. "반복가능한 것의 각 항목에 식을 적용해 새 리스트를 만든다".구조 뜯어보기
[ n ** 2 for n in range(1, 6) ] ──┬── ────────┬────────── 식 반복 (for ~ in ~) (각 항목을 (8장의 for와 무엇으로 똑같은 모양) 바꿀지)
읽는 순서는 "range(1,6)의 각 n에 대해, n ** 2를 모아 리스트로"입니다. 오른쪽 for 부분을 먼저 읽고, 왼쪽 식을 나중에 읽으면 자연스럽습니다.
루프와 컴프리헨션 1:1로 맞춰 보기
처음엔 "어느 부분이 어디서 왔는지" 헷갈립니다. for 루프와 컴프리헨션을 같은 색끼리 맞춰 보면 분명해집니다.
# 일반 for 루프 result = [] # ① 빈 리스트 for n in range(1, 6): # ② 반복 result.append(n ** 2) # ③ 변환해서 추가 # 컴프리헨션 — 위 세 줄을 한 줄로 result = [n ** 2 for n in range(1, 6)] # └──③──┘ └─────②──────┘ # (추가할 식) (반복 부분) # ①(리스트로 모으는 건 대괄호가 담당)
대응 관계를 표로 보면 이렇습니다.
| 일반 루프 | 컴프리헨션 위치 | 역할 |
|---|---|---|
result = [] | 바깥 [ ] | 결과를 리스트로 모음 |
for n in range(1,6): | 가운데 for n in range(1,6) | 반복 (그대로!) |
result.append(n**2) | 맨 앞 n ** 2 | 각 항목을 무엇으로 바꿀지 |
for부터 적고, 그다음 맨 앞 식을 채운다"는 순서로 생각하면 쉽습니다. "무엇을 반복하지?(for n in ...) → 각각을 어떻게 바꾸지?(n ** 2)" 순입니다.문자열도 반복 가능하니 이렇게도 됩니다.
chars = [c.upper() for c in "python"] print(chars) # ['P', 'Y', 'T', 'H', 'O', 'N']
조건 필터: if 붙이기
뒤에 if 조건을 붙이면, 조건을 만족하는 항목만 골라 담습니다.
evens = [n for n in range(10) if n % 2 == 0] print(evens) # [0, 2, 4, 6, 8]
변환과 필터를 함께 쓸 수도 있습니다.
words = ["hi", "Python", "a", "world"] result = [w.upper() for w in words if len(w) > 2] print(result) # ['PYTHON', 'WORLD'] (3글자 이상만 골라 대문자로)
읽는 법: "words의 각 w 중 길이가 2 초과인 것만 골라, 대문자로 바꿔 모아라".
if는 필터다. [식 for 변수 in 반복 if 조건] = "조건을 통과한 항목만 식을 적용".변환 시 분기: 삼항 표현식
"걸러내기"가 아니라 "값에 따라 다르게 변환"하려면 (초급편 7장의) 삼항 표현식을 식 자리에 씁니다.
labels = ["짝" if n % 2 == 0 else "홀" for n in range(5)] print(labels) # ['짝', '홀', '짝', '홀', '짝']
if가 어디 있느냐로 의미가 완전히 다릅니다.[n for n in range(5) if n % 2 == 0]→ 필터(뒤의 if): 짝수만 남김 →[0, 2, 4]["짝" if n%2==0 else "홀" for n in range(5)]→ 변환 분기(앞의 삼항): 전부 남기되 라벨 변경
"거르려면 뒤에
if, 값을 바꾸려면 앞에 if...else"로 기억하세요.딕셔너리·세트 컴프리헨션
대괄호 대신 중괄호를 쓰면 딕셔너리나 세트가 됩니다.
딕셔너리 컴프리헨션
{키: 값 for ...} 형태입니다.
sq_dict = {n: n ** 2 for n in range(1, 4)}
print(sq_dict) # {1: 1, 2: 4, 3: 9}
prices = {"사과": 1000, "배": 3000, "감": 500}
# 값을 10% 인상한 새 딕셔너리
raised = {name: price * 1.1 for name, price in prices.items()}
print(raised) # {'사과': 1100.0..., '배': 3300.0..., '감': 550.0...}
세트 컴프리헨션
{값 for ...} 형태(콜론 없음). 중복이 자동으로 제거됩니다.
dup = [1, 2, 2, 3, 3, 3] unique_squares = {n ** 2 for n in dup} print(sorted(unique_squares)) # [1, 4, 9] (중복 제거됨)
| 컴프리헨션 | 기호 | 형태 | 결과 |
|---|---|---|---|
| 리스트 | [ ] | [식 for ...] | list |
| 딕셔너리 | { } | {키: 값 for ...} | dict |
| 세트 | { } | {식 for ...} | set |
zip: 두 리스트를 짝짓기
zip()은 여러 시퀀스를 같은 위치끼리 묶어줍니다. 지퍼가 양쪽 이를 맞물리는 모습을 떠올리세요.
names = ["민지", "현우", "수빈"] ages = [25, 30, 28] for name, age in zip(names, ages): print(f"{name}: {age}살") # 민지: 25살 # 현우: 30살 # 수빈: 28살
컴프리헨션과 결합하면 두 리스트로 딕셔너리를 만드는 게 한 줄입니다.
people = {name: age for name, age in zip(names, ages)}
print(people) # {'민지': 25, '현우': 30, '수빈': 28}
zip은 가장 짧은 시퀀스 길이에 맞춰 멈춥니다. 길이가 다르면 짧은 쪽 기준으로 잘립니다.enumerate: 번호와 함께
초급편 8장에서 본 enumerate()도 컴프리헨션에서 유용합니다. 번호가 필요할 때 씁니다.
names = ["민지", "현우"] numbered = [f"{i+1}. {name}" for i, name in enumerate(names)] print(numbered) # ['1. 민지', '2. 현우']
💡 제너레이터 표현식: 메모리를 아끼는 사촌
대괄호 대신 소괄호를 쓰면 제너레이터 표현식이 됩니다. 리스트는 모든 값을 즉시 만들어 메모리에 담지만, 제너레이터는 필요할 때 하나씩 만들어내 메모리를 아낍니다.
# 리스트: 100만 개를 전부 메모리에 만듦 squares_list = [n ** 2 for n in range(1_000_000)] # 제너레이터: 값을 미리 만들지 않고, 요청할 때 하나씩 squares_gen = (n ** 2 for n in range(1_000_000)) print(type(squares_gen)) # <class 'generator'>
sum()처럼 한 번 훑고 버리면 되는 경우엔 제너레이터 표현식이 효율적입니다. 대괄호 없이 함수에 바로 넣을 수 있습니다.
total = sum(n ** 2 for n in range(5)) # 리스트를 안 만들고 합산 print(total) # 30
📎 제너레이터의 동작 원리(yield, 지연 평가)는 다음 14장에서 자세히 다룹니다. 지금은 "큰 데이터를 한 번만 훑을 땐( )제너레이터가 메모리에 좋다" 정도면 충분합니다.
나쁜 예 ❌ vs 좋은 예 ✅
nums = [1, 2, 3, 4, 5, 6] # ❌ 나쁜 예: 단순 변환에 장황한 루프 result = [] for n in nums: if n % 2 == 0: result.append(n * 10) # ✅ 좋은 예: 컴프리헨션 한 줄 result = [n * 10 for n in nums if n % 2 == 0] print(result) # [20, 40, 60]
for 루프가 낫습니다. "한눈에 읽히는가"가 기준입니다.이 장에서 배운 것
- 리스트 컴프리헨션
[식 for 변수 in 반복]은 "반복하며 새 리스트 만들기"를 한 줄로 쓴다. - 뒤의
if 조건은 필터, 식 자리의 삼항A if 조건 else B는 변환 분기다. 위치로 의미가 갈린다. - 중괄호로 딕셔너리(
{키:값 for})·세트({식 for}) 컴프리헨션을 만든다. zip으로 여러 시퀀스를 짝짓고,enumerate로 번호를 얻는다.- 소괄호 제너레이터 표현식
(식 for ...)은 값을 하나씩 만들어 메모리를 아낀다. - 복잡해지면 컴프리헨션보다 일반 루프가 낫다 — 가독성이 기준이다.
🧪 실습 문제
문제 1. 컴프리헨션으로 1부터 10까지 각 수의 세제곱 리스트를 만들어 출력하세요. (결과: [1, 8, 27, ...])
문제 2. 리스트 nums = [15, 22, 8, 41, 6, 30]에서 20 이상인 수만 골라 새 리스트를 컴프리헨션으로 만드세요.
문제 3. 다음 두 컴프리헨션의 결과 차이를 설명하고 각각의 출력을 적으세요.
a = [n for n in range(6) if n % 2 == 0] b = ["E" if n % 2 == 0 else "O" for n in range(6)]
문제 4. 단어 리스트 words = ["apple", "kiwi", "banana", "fig"]로, {단어: 글자수} 딕셔너리를 컴프리헨션으로 만드세요.
문제 5. 두 리스트 subjects = ["국어", "수학", "영어"]와 scores = [90, 85, 95]를 zip과 컴프리헨션으로 묶어 {"국어": 90, ...} 딕셔너리를 만드세요.
<details>
<summary>✅ 정답·해설 보기</summary>
1.
cubes = [n ** 3 for n in range(1, 11)] print(cubes) # [1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
2.
nums = [15, 22, 8, 41, 6, 30] big = [n for n in nums if n >= 20] print(big) # [22, 41, 30]
3.
a: 뒤의if는 필터라 짝수만 남김 →[0, 2, 4]b: 앞의 삼항은 전부 남기되 라벨 변환 →['E', 'O', 'E', 'O', 'E', 'O']
4.
words = ["apple", "kiwi", "banana", "fig"] lengths = {w: len(w) for w in words} print(lengths) # {'apple': 5, 'kiwi': 4, 'banana': 6, 'fig': 3}
5.
subjects = ["국어", "수학", "영어"] scores = [90, 85, 95] report = {s: sc for s, sc in zip(subjects, scores)} print(report) # {'국어': 90, '수학': 85, '영어': 95}
</details>
◀️ 이전 장: 12. 상속과 매직 메서드 | ▶️ 다음 장: 14. 이터레이터와 제너레이터