결론: 새 컨테이너를 만드는 짧은 규칙에 쓴다
컴프리헨션(comprehension)은 iterable의 항목을 변환하거나 필터링해 새 list·set·dict를 만드는 표현식이다. 결과 생성 규칙을 한눈에 읽을 수 있을 때 사용하고, 예외 처리·여러 상태 변경·긴 분기가 필요하면 일반 반복문으로 돌아간다.
# 일반 반복문
squares = []
for number in range(5):
squares.append(number ** 2)
# 같은 생성 규칙의 list comprehension
squares = [number ** 2 for number in range(5)]
print(squares)
[0, 1, 4, 9, 16]
기본 모양은 [출력식 for 항목 in iterable]이다. 오른쪽에서 항목을 순회하고 왼쪽 출력식을 평가해 새 list에 순서대로 넣는다. 원본 iterable의 순서와 중복은 그대로 반영된다.
list comprehension으로 변환하기
raw_names = [" ada ", "Linus", " ada "]
normalized = [name.strip().upper() for name in raw_names]
print(normalized)
['ADA', 'LINUS', 'ADA']
각 문자열에 strip과 upper를 적용했지만 순서와 중복은 유지되었다. 여러 함수를 길게 연결해 의미가 흐려지면 이름 있는 정제 함수로 분리한 뒤 [normalize(name) for name in raw_names]처럼 쓴다.
뒤의 if는 필터, 앞의 if·else는 값 선택
[expr for x in data if condition]에서 뒤쪽 if는 조건을 통과한 항목만 결과에 넣는 필터다. [a if condition else b for x in data]의 앞쪽 조건 표현식은 모든 항목을 유지하면서 출력값을 둘 중 하나로 고른다.
scores = [95, -1, 72, 40]
valid_scores = [score for score in scores if score >= 0]
labels = ["pass" if score >= 60 else "fail" for score in valid_scores]
print(valid_scores)
print(labels)
[95, 72, 40]
['pass', 'pass', 'fail']
첫 comprehension은 -1을 제외해 결과 길이가 줄었다. 두 번째는 남은 각 점수마다 label 하나를 만들어 길이를 유지했다. else 없는 조건 표현식은 문법이 아니며, 필터 if에는 else가 필요 없다.
dict comprehension으로 키와 값을 함께 변환하기
{key_expr: value_expr for item in iterable}은 key-value 관계를 새 dict로 만든다. 기존 dict를 변환할 때는 보통 items()로 키와 값을 함께 순회한다.
prices = {"apple": 1000, "pear": 2000}
discounted = {
name: int(price * 0.9)
for name, price in prices.items()
if price >= 1500
}
print(discounted)
rows = [("a", 1), ("a", 2), ("b", 3)]
latest = {key: value for key, value in rows}
print(latest)
{'pear': 1800}
{'a': 2, 'b': 3}
dict 키는 고유하므로 comprehension이 같은 키를 여러 번 만들면 나중에 저장된 값이 앞의 값을 대체한다. 중복 행을 모두 보존해야 한다면 이 방식은 데이터 손실을 만든다. 그때는 defaultdict(list)처럼 그룹핑 의도가 드러나는 구조를 사용한다. 키와 값이 별도 iterable이면 dict(zip(keys, values, strict=True))도 간결하다.
set comprehension으로 고유한 변환 결과 만들기
numbers = [-2, -1, 1, 2, 2]
unique_squares = {number ** 2 for number in numbers}
print(sorted(unique_squares))
[1, 4]
-2와 2, -1과 1이 같은 제곱을 만들어 중복이 제거되었다. set은 순서를 보장하지 않으므로 출력 예제는 sorted로 순서를 명시했다. 순서와 중복을 보존해야 한다면 list comprehension을 선택한다.
다중 for는 같은 순서의 중첩 반복문이다
comprehension의 여러 for와 if는 일반 중첩 반복문을 쓸 때와 같은 왼쪽에서 오른쪽 순서로 읽는다.
pairs = [
(number, letter)
for number in [1, 2]
for letter in ["a", "b"]
]
print(pairs)
# 위와 같은 순서
expanded = []
for number in [1, 2]:
for letter in ["a", "b"]:
expanded.append((number, letter))
print(pairs == expanded)
[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]
True
다중 for에 조건과 함수 호출이 계속 붙어 한 번에 해석하기 어렵다면 일반 반복문이 낫다. 행 수가 큰 두 iterable의 모든 조합은 결과 컨테이너도 커지므로 문법의 간결함과 메모리 비용을 혼동하지 않는다.
comprehension 변수는 별도 scope에 있다
name = "outside"
upper_names = [name.upper() for name in ["ada", "linus"]]
print(name)
print(upper_names)
outside
['ADA', 'LINUS']
Python 3에서 왼쪽 첫 iterable 표현식을 제외한 comprehension 본문은 암시적인 별도 scope에서 실행된다. 그래서 반복 변수 name이 바깥 name의 바인딩을 덮어쓰지 않았다. 그래도 같은 이름을 과도하게 재사용하면 독자가 혼동하므로 의미 있는 항목 이름을 쓴다.
언제 일반 반복문을 선택할까
예외 종류별 처리, 여러 누적 상태 변경, 단계별 로그와 디버깅, break·continue가 필요하면 일반 반복문이 더 직접적이다. comprehension 안에서 print·append·파일 쓰기 같은 부작용만 일으키고 생성된 결과를 버리는 패턴도 피한다.
컴프리헨션이 무조건 더 빠르다고 가정하지 않는다. 핵심 장점은 새 컨테이너를 만드는 변환·필터 의도를 간결하게 표현하는 것이다. 실제 병목은 데이터로 측정하고, 읽기 어려워지는 순간 일반 반복문이나 함수로 분리한다.
list·set·dict comprehension은 결과 컨테이너를 즉시 만든다. 대용량 데이터를 하나씩 지연 처리해야 한다면 괄호를 사용하는 generator expression을 검토한다. generator와 yield는 11번 글에서 다룬다.
종합 실습: 활성 행 정제하기
raw_rows = [
{"name": " Ada ", "score": "90", "active": True},
{"name": "", "score": "bad", "active": True},
{"name": "Linus", "score": "0", "active": False},
]
cleaned = [
{"name": row["name"].strip(), "score": int(row["score"])}
for row in raw_rows
if row["active"]
and row["name"].strip()
and row["score"].isdigit()
]
print(cleaned)
[{'name': 'Ada', 'score': 90}]
이 예제는 활성 여부, 빈 이름, 숫자 문자열을 필터한 뒤 새 dict 행을 만든다. 실제 파이프라인에서 제외 이유를 기록하거나 잘못된 점수를 별도로 수집해야 한다면 한 줄의 결과만 만드는 comprehension보다 일반 반복문이 더 적합하다.
흔한 실수와 핵심 체크리스트
☐ comprehension이 기존 컨테이너를 수정하는 문장이 아니라 새 컨테이너를 만드는 표현식임을 안다.
☐ 뒤쪽 if는 항목 필터, 앞쪽 a if cond else b는 출력값 선택임을 구분한다.
☐ list는 순서·중복 유지, set은 중복 제거·순서 미보장, dict는 key-value·고유 키라는 결과 특성을 선택한다.
☐ 다중 for를 같은 순서의 중첩 반복문으로 펼쳐 설명할 수 있다.
☐ 부작용, 예외 처리, 여러 상태, 긴 조건이 있으면 일반 반복문을 선택한다.
☐ 대용량 lazy 처리가 필요하면 즉시 생성 comprehension과 generator를 구분한다.
다음 글: 문자열 슬라이싱과 포매팅
컴프리헨션에서 문자열을 정제했으니 다음 글에서는 문자열의 인덱싱·슬라이싱, 불변성, 주요 메서드와 f-string으로 분석 결과를 안전하게 표현하는 법을 다룬다.
참고 자료
Python list comprehension 공식 튜토리얼 — 변환·필터 문법과 일반 반복문 대응 (2026-08-03 확인)
Python nested comprehension 공식 튜토리얼 — 다중·중첩 comprehension과 일반 반복문 변환 (2026-08-03 확인)
Python list·set·dict display 언어 레퍼런스 — 새 컨테이너 생성, 평가 순서와 comprehension scope (2026-08-03 확인)
Python conditional expressions 언어 레퍼런스 — a if condition else b의 평가 방식 (2026-08-03 확인)
댓글 0
댓글을 불러오는 중…