Skip to main content

컴프리헨션 (Comprehension)

학습 목표

  • 리스트 컴프리헨션의 구조와 동작 방식을 이해한다
  • 조건 필터링과 중첩 컴프리헨션을 활용할 수 있다
  • 딕셔너리와 집합 컴프리헨션을 사용할 수 있다
  • 컴프리헨션의 적절한 사용 범위를 판단할 수 있다

왜 중요한가

컴프리헨션은 Python의 대표적인 관용 표현(Pythonic idiom)입니다. for 루프로 작성할 수 있는 데이터 변환을 한 줄로 간결하게 표현합니다. ML/DL 코드에서 데이터 전처리, 특성 추출, 결과 변환 등에 광범위하게 사용됩니다.

리스트 컴프리헨션

기본 구조

# 기본 형태: [표현식 for 변수 in 반복가능객체]

# for 루프 방식
squares = []
for x in range(10):
    squares.append(x ** 2)

# 리스트 컴프리헨션
squares = [x ** 2 for x in range(10)]
print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

조건 필터링

# [표현식 for 변수 in 반복가능객체 if 조건]

# 짝수만 추출
evens = [x for x in range(20) if x % 2 == 0]
print(evens)  # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# 양수만 제곱
numbers = [-3, -1, 0, 2, 4, -5, 7]
positive_squares = [x ** 2 for x in numbers if x > 0]
print(positive_squares)  # [4, 16, 49]

# if-else (표현식 위치에 작성)
labels = [x for x in range(10)]
categories = ["짝수" if x % 2 == 0 else "홀수" for x in range(5)]
print(categories)  # ["짝수", "홀수", "짝수", "홀수", "짝수"]
if만 있는 경우 뒤에 배치하고(필터링), if-else앞에 배치합니다(변환).
[x for x in data if x > 0]           # 필터링: if만 뒤에
["양수" if x > 0 else "음수" for x in data]  # 변환: if-else 앞에

중첩 컴프리헨션

# 2차원 -> 1차원 (flatten)
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
print(flat)  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 위 코드는 아래와 동일
flat = []
for row in matrix:
    for x in row:
        flat.append(x)

# 2차원 리스트 생성
grid = [[i * 3 + j + 1 for j in range(3)] for i in range(3)]
print(grid)  # [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# 조합 생성
pairs = [(x, y) for x in range(3) for y in range(3) if x != y]
print(pairs)  # [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]

딕셔너리 컴프리헨션

# {키_표현식: 값_표현식 for 변수 in 반복가능객체}

# 제곱 딕셔너리
squares = {x: x ** 2 for x in range(6)}
print(squares)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 키-값 뒤집기
original = {"a": 1, "b": 2, "c": 3}
inverted = {v: k for k, v in original.items()}
print(inverted)  # {1: "a", 2: "b", 3: "c"}

# 조건 필터링
scores = {"김철수": 85, "이영희": 92, "박민수": 67, "최지원": 78}
passed = {name: score for name, score in scores.items() if score >= 80}
print(passed)  # {"김철수": 85, "이영희": 92}

# 문자열 처리
words = ["hello", "world", "python"]
word_lengths = {word: len(word) for word in words}
print(word_lengths)  # {"hello": 5, "world": 5, "python": 6}

집합 컴프리헨션

# {표현식 for 변수 in 반복가능객체}

# 고유한 글자 추출
text = "hello world"
unique_chars = {char for char in text if char != " "}
print(unique_chars)  # {'h', 'e', 'l', 'o', 'w', 'r', 'd'}

# 나머지값 집합
remainders = {x % 5 for x in range(20)}
print(remainders)  # {0, 1, 2, 3, 4}

제너레이터 표현식

소괄호 ()를 사용하면 제너레이터 표현식(Generator Expression)이 됩니다. 메모리를 절약하면서 지연 평가(Lazy Evaluation)를 수행합니다.
# 리스트 컴프리헨션 - 모든 요소를 메모리에 저장
squares_list = [x ** 2 for x in range(1_000_000)]  # 메모리 많이 사용

# 제너레이터 표현식 - 필요할 때만 생성
squares_gen = (x ** 2 for x in range(1_000_000))   # 메모리 거의 사용 안 함

# sum, min, max 등에 직접 전달 가능
total = sum(x ** 2 for x in range(100))
print(total)  # 328350

AI/ML에서의 활용

# 특성 추출
raw_data = ["  Hello World  ", "  Python ML  ", "  Deep Learning  "]
features = [text.strip().lower() for text in raw_data]
print(features)  # ["hello world", "python ml", "deep learning"]

# 원-핫 인코딩 벡터 생성
labels = ["cat", "dog", "bird"]
label = "dog"
one_hot = [1 if l == label else 0 for l in labels]
print(one_hot)  # [0, 1, 0]

# 하이퍼파라미터 그리드 생성
learning_rates = [0.001, 0.01, 0.1]
batch_sizes = [16, 32, 64]
grid = [
    {"lr": lr, "batch_size": bs}
    for lr in learning_rates
    for bs in batch_sizes
]
print(f"총 {len(grid)}개 조합")  # 총 9개 조합

# 파일 목록 필터링
import os
files = os.listdir(".")
python_files = [f for f in files if f.endswith(".py")]
컴프리헨션은 간결하지만, 너무 복잡해지면 가독성이 떨어집니다. 3중 이상 중첩이나 복잡한 조건은 일반 for 루프로 작성하는 것이 좋습니다.
# 나쁜 예 - 너무 복잡한 컴프리헨션
result = [f(x) for x in data if g(x) for y in h(x) if p(y)]

# 좋은 예 - 루프로 풀어쓰기
result = []
for x in data:
    if g(x):
        for y in h(x):
            if p(y):
                result.append(f(x))
일반적으로 약간 더 빠릅니다. CPython에서 컴프리헨션은 내부적으로 최적화된 바이트코드를 생성합니다. 하지만 성능 차이보다는 가독성이 선택 기준이 되어야 합니다.
Python 3.8부터 가능합니다. 컴프리헨션 내에서 중간 계산 결과를 재사용할 때 유용합니다.
# 계산 결과가 조건에도, 값에도 필요할 때
results = [y for x in data if (y := expensive_func(x)) > threshold]

체크리스트

  • 리스트 컴프리헨션의 구조를 정확히 작성할 수 있다
  • 필터링(if)과 변환(if-else)의 위치 차이를 구분할 수 있다
  • 딕셔너리, 집합 컴프리헨션을 사용할 수 있다
  • 컴프리헨션의 가독성 한계를 인지하고 적절히 사용할 수 있다

다음 문서