[2026] Python 컴프리헨션 | 리스트, 딕셔너리, 세트 컴프리헨션 완벽 정리
이 글의 핵심
Python 컴프리헨션: 리스트, 딕셔너리, 세트 컴프리헨션 리스트 컴프리헨션 (List Comprehension)부터 핵심 개념·패턴·실무 함정을 코드 예제로 풉니다.
들어가며
”한 줄로 리스트 만들기”
컴프리헨션은 간결하고 빠른 Python의 강력한 기능입니다.
실무 활용 사례: 데이터 분석, 웹 개발, 자동화 프로젝트에서 실제로 사용한 패턴과 코드를 바탕으로 정리했습니다. 초보자가 흔히 겪는 오류와 해결법을 포함합니다.
실무에서 느낀 Python의 매력
처음 Python을 배울 때는 “이게 정말 프로그래밍 언어인가?” 싶을 정도로 간결했습니다. C++에서 10줄로 작성하던 코드가 Python에서는 2~3줄로 끝나는 경우가 많았죠. 특히 데이터 분석 프로젝트를 진행하면서 Pandas와 NumPy의 강력함을 체감했습니다. 엑셀로 몇 시간 걸리던 작업이 Python 스크립트로는 몇 초 만에 끝나는 걸 보고 동료들이 놀라워했던 기억이 납니다. 하지만 처음부터 순탄하지만은 않았습니다. 들여쓰기 하나 잘못해서 몇 시간을 헤맨 적도 있고, 가상환경 설정이 꼬여서 프로젝트 전체를 다시 시작한 적도 있습니다. 이런 시행착오를 겪으며 깨달은 건, 환경 설정을 처음부터 제대로 하는 것이 얼마나 중요한지였습니다. 이 글에서는 제가 겪은 실수들을 바탕으로, 여러분이 같은 시행착오를 겪지 않도록 실전 팁을 담았습니다.
1. 리스트 컴프리헨션 (List Comprehension)
리스트 컴프리헨션이란?
리스트 컴프리헨션은 한 줄로 리스트를 생성하는 Python의 강력한 기능입니다. 일반 for 루프보다 간결하고 빠릅니다.
문법: [표현식 for 변수 in 반복가능객체 if 조건]
기본 문법과 동작 원리
아래 왼쪽은 빈 장바구니에 for 루프로 하나씩 담는 방식이고, 오른쪽 한 줄은 같은 일을 컴프리헨션으로 압축한 것입니다. 읽을 때는 for i in range(10)을 먼저 읽고, 앞쪽 i ** 2가 각 반복에서 리스트에 들어간다고 보면 됩니다.
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 일반 for 루프 (전통적 방식)
squares = []
for i in range(10):
squares.append(i ** 2)
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 리스트 컴프리헨션 (Python 스타일)
squares = [i ** 2 for i in range(10)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 동작 과정:
# 1. range(10)에서 i를 하나씩 가져옴 (0, 1, 2, ..., 9)
# 2. 각 i에 대해 i ** 2 계산
# 3. 결과를 리스트에 추가
# 4. 최종 리스트 반환
성능 비교: 다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import time
# 방법 1: for 루프
start = time.time()
result1 = []
for i in range(1000000):
result1.append(i ** 2)
print(f"for 루프: {time.time() - start:.4f}초") # 약 0.15초
# 방법 2: 리스트 컴프리헨션
start = time.time()
result2 = [i ** 2 for i in range(1000000)]
print(f"컴프리헨션: {time.time() - start:.4f}초") # 약 0.10초
# 컴프리헨션이 약 30% 빠름!
조건문 추가 (필터링)
if 조건으로 특정 요소만 선택할 수 있습니다. 다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 짝수만 선택
evens = [i for i in range(10) if i % 2 == 0]
print(evens) # [0, 2, 4, 6, 8]
# 동작 과정:
# i=0: 0 % 2 == 0 → True → 0 추가
# i=1: 1 % 2 == 0 → False → 무시
# i=2: 2 % 2 == 0 → True → 2 추가
# ...
# 3의 배수이면서 10보다 큰 수
multiples = [i for i in range(30) if i % 3 == 0 and i > 10]
print(multiples) # [12, 15, 18, 21, 24, 27]
# 문자열 필터링
words = ['apple', 'banana', 'cherry', 'date', 'elderberry']
long_words = [word for word in words if len(word) > 5]
print(long_words) # ['banana', 'cherry', 'elderberry']
if-else 표현식 (변환)
if-else를 사용하여 요소를 변환할 수 있습니다. 다음은 python를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 짝수/홀수 라벨링
labels = ['짝수' if i % 2 == 0 else '홀수' for i in range(5)]
print(labels) # ['짝수', '홀수', '짝수', '홀수', '짝수']
# 문법 주의: if-else는 for 앞에
# [표현식 if 조건 else 다른표현식 for 변수 in 반복가능객체]
# 양수/음수/0 분류
numbers = [-2, -1, 0, 1, 2]
signs = [
'양수' if n > 0 else ('음수' if n < 0 else '0')
for n in numbers
]
print(signs) # ['음수', '음수', '0', '양수', '양수']
# 점수를 등급으로 변환
scores = [95, 85, 75, 65, 55]
grades = [
'A' if s >= 90 else 'B' if s >= 80 else 'C' if s >= 70 else 'D' if s >= 60 else 'F'
for s in scores
]
print(grades) # ['A', 'B', 'C', 'D', 'F']
if vs if-else 위치 차이: 아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 필터링 (if만): for 뒤에
evens = [i for i in range(10) if i % 2 == 0]
# 변환 (if-else): for 앞에
labels = ['짝수' if i % 2 == 0 else '홀수' for i in range(10)]
# 필터링 + 변환: 둘 다 사용
# [변환표현식 for 변수 in 반복 if 필터조건]
positive_squares = [i ** 2 if i > 0 else 0 for i in range(-5, 6) if i != 0]
print(positive_squares)
중첩 반복문 (Nested Loops)
중첩 for 루프를 한 줄로 표현할 수 있습니다. 다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 2차원 리스트 평탄화 (Flatten)
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# 일반 for 루프
flat = []
for row in matrix:
for num in row:
flat.append(num)
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 리스트 컴프리헨션 (왼쪽 for가 외부, 오른쪽 for가 내부)
flat = [num for row in matrix for num in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 읽는 순서:
# for row in matrix: # 외부 루프
# for num in row: # 내부 루프
# num # 표현식
# 구구단 생성
multiplication_table = [
f"{i} x {j} = {i*j}"
for i in range(2, 10) # 2단부터 9단
for j in range(1, 10) # 1부터 9까지
]
print(multiplication_table[:5])
# ['2 x 1 = 2', '2 x 2 = 4', '2 x 3 = 6', '2 x 4 = 8', '2 x 5 = 10']
# 좌표 생성
coordinates = [(x, y) for x in range(3) for y in range(3)]
print(coordinates)
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
# 조건 추가: 대각선만
diagonal = [(x, y) for x in range(5) for y in range(5) if x == y]
print(diagonal) # [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
중첩 컴프리헨션 vs 중첩 리스트: 다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 2차원 리스트 생성 (행렬)
# 방법 1: 중첩 컴프리헨션
matrix = [[i * j for j in range(5)] for i in range(5)]
print(matrix)
# [[0, 0, 0, 0, 0],
# [0, 1, 2, 3, 4],
# [0, 2, 4, 6, 8],
# [0, 3, 6, 9, 12],
# [0, 4, 8, 12, 16]]
# 방법 2: 일반 for 루프
matrix = []
for i in range(5):
row = []
for j in range(5):
row.append(i * j)
matrix.append(row)
2. 딕셔너리 컴프리헨션 (Dictionary Comprehension)
딕셔너리 컴프리헨션이란?
딕셔너리 컴프리헨션은 키-값 쌍을 한 줄로 생성하는 기능입니다.
문법: {키표현식: 값표현식 for 변수 in 반복가능객체 if 조건}
기본 문법과 동작 원리
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 일반 for 루프
squares_dict = {}
for i in range(5):
squares_dict[i] = i ** 2
print(squares_dict) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 딕셔너리 컴프리헨션
squares_dict = {i: i ** 2 for i in range(5)}
print(squares_dict) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 동작 과정:
# i=0: 키=0, 값=0**2=0 → {0: 0}
# i=1: 키=1, 값=1**2=1 → {0: 0, 1: 1}
# i=2: 키=2, 값=2**2=4 → {0: 0, 1: 1, 2: 4}
# ...
# 리스트를 딕셔너리로 변환
names = ['철수', '영희', '민수']
name_dict = {i: name for i, name in enumerate(names)}
print(name_dict) # {0: '철수', 1: '영희', 2: '민수'}
# enumerate() 설명:
# enumerate(names) = [(0, '철수'), (1, '영희'), (2, '민수')]
# 각 (i, name) 튜플에서 i는 키, name은 값
조건부 딕셔너리 (필터링)
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 짝수만 딕셔너리에 추가
even_squares = {i: i ** 2 for i in range(10) if i % 2 == 0}
print(even_squares) # {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
# 특정 값만 필터링
scores = {'철수': 85, '영희': 92, '민수': 78, '지영': 95}
high_scores = {name: score for name, score in scores.items() if score >= 90}
print(high_scores) # {'영희': 92, '지영': 95}
# 키와 값 모두 조건 적용
data = {'apple': 5, 'banana': 3, 'cherry': 8, 'date': 2}
filtered = {
k.upper(): v * 2
for k, v in data.items()
if len(k) > 4 and v > 3
}
print(filtered) # {'APPLE': 10, 'CHERRY': 16}
키-값 변환 (Swap)
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 키와 값 교환
original = {'a': 1, 'b': 2, 'c': 3}
swapped = {v: k for k, v in original.items()}
print(swapped) # {1: 'a', 2: 'b', 3: 'c'}
# 주의: 값이 중복되면 마지막 값만 남음
original = {'a': 1, 'b': 2, 'c': 1}
swapped = {v: k for k, v in original.items()}
print(swapped) # {1: 'c', 2: 'b'} ('a'는 'c'에 덮어씌워짐)
# 해결: 값을 리스트로 수집
from collections import defaultdict
swapped_multi = defaultdict(list)
for k, v in original.items():
swapped_multi[v].append(k)
print(dict(swapped_multi)) # {1: ['a', 'c'], 2: ['b']}
실전 예제: 데이터 변환
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 예제 1: 문자열 길이 매핑
words = ['apple', 'banana', 'cherry', 'date']
word_lengths = {word: len(word) for word in words}
print(word_lengths)
# {'apple': 5, 'banana': 6, 'cherry': 6, 'date': 4}
# 예제 2: 환경 변수 파싱
env_str = "DEBUG=True,PORT=8000,HOST=localhost"
env_dict = {
pair.split('=')[0]: pair.split('=')[1]
for pair in env_str.split(',')
}
print(env_dict)
# {'DEBUG': 'True', 'PORT': '8000', 'HOST': 'localhost'}
# 예제 3: 두 리스트를 딕셔너리로 결합
keys = ['name', 'age', 'city']
values = ['Alice', 25, 'Seoul']
person = {k: v for k, v in zip(keys, values)}
print(person) # {'name': 'Alice', 'age': 25, 'city': 'Seoul'}
# 예제 4: 딕셔너리 필터링 및 변환
products = {
'apple': 1000,
'banana': 500,
'cherry': 2000,
'date': 800
}
# 1000원 이상 제품에 10% 할인
discounted = {
name: price * 0.9
for name, price in products.items()
if price >= 1000
}
print(discounted) # {'apple': 900.0, 'cherry': 1800.0}
3. 세트 컴프리헨션 (Set Comprehension)
세트 컴프리헨션이란?
세트 컴프리헨션은 중복 없는 집합을 한 줄로 생성합니다. 리스트 컴프리헨션과 비슷하지만 [] 대신 {}를 사용합니다.
문법: {표현식 for 변수 in 반복가능객체 if 조건}
기본 문법과 중복 제거
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 중복이 있는 리스트
numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
# 일반 방법: set() 사용
unique = set(numbers)
print(unique) # {1, 2, 3, 4}
# 세트 컴프리헨션
unique = {n for n in numbers}
print(unique) # {1, 2, 3, 4}
# 변환 + 중복 제거
numbers = [1, -2, 2, -3, 3, -4, 4]
abs_unique = {abs(n) for n in numbers}
print(abs_unique) # {1, 2, 3, 4} (음수가 양수로 변환되며 중복 제거)
조건부 세트
아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 짝수만
even_set = {i for i in range(10) if i % 2 == 0}
print(even_set) # {0, 2, 4, 6, 8}
# 문자열에서 모음만 추출
text = "Hello World"
vowels = {char.lower() for char in text if char.lower() in 'aeiou'}
print(vowels) # {'e', 'o'} (중복 'o' 자동 제거)
# 리스트에서 길이 3 이상 단어만
words = ['hi', 'hello', 'hey', 'hello', 'world', 'hi']
long_words = {word for word in words if len(word) >= 3}
print(long_words) # {'hello', 'hey', 'world'} (중복 제거됨)
실전 예제
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 예제 1: 이메일 도메인 추출
emails = [
'alice@gmail.com',
'bob@yahoo.com',
'charlie@gmail.com',
'david@outlook.com',
'eve@gmail.com'
]
domains = {email.split('@')[1] for email in emails}
print(domains) # {'gmail.com', 'yahoo.com', 'outlook.com'}
# 예제 2: 파일 확장자 수집
files = ['image.jpg', 'doc.pdf', 'photo.jpg', 'video.mp4', 'report.pdf']
extensions = {file.split('.')[-1] for file in files}
print(extensions) # {'jpg', 'pdf', 'mp4'}
# 예제 3: 숫자의 마지막 자리 수
numbers = [123, 456, 789, 111, 222, 333]
last_digits = {n % 10 for n in numbers}
print(last_digits) # {1, 2, 3, 6, 9}
세트 연산과 결합
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 두 리스트의 공통 요소
list1 = [1, 2, 3, 4, 5]
list2 = [4, 5, 6, 7, 8]
common = {x for x in list1} & {x for x in list2}
print(common) # {4, 5}
# 또는 더 간단하게
common = set(list1) & set(list2)
print(common) # {4, 5}
# 차집합: list1에만 있는 요소
diff = {x for x in list1} - {x for x in list2}
print(diff) # {1, 2, 3}
# 합집합
union = {x for x in list1} | {x for x in list2}
print(union) # {1, 2, 3, 4, 5, 6, 7, 8}
4. 제너레이터 표현식 (Generator Expression)
제너레이터 표현식이란?
제너레이터 표현식은 필요할 때만 값을 생성하는 메모리 효율적인 방법입니다. 리스트 컴프리헨션과 문법이 같지만 [] 대신 ()를 사용합니다.
문법: (표현식 for 변수 in 반복가능객체 if 조건)
기본 문법과 동작 원리
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 리스트 컴프리헨션: 모든 값을 메모리에 저장
squares_list = [i ** 2 for i in range(1000000)]
print(type(squares_list)) # <class 'list'>
print(len(squares_list)) # 1000000 (모든 요소가 메모리에 존재)
# 제너레이터 표현식: 필요할 때만 생성
squares_gen = (i ** 2 for i in range(1000000))
print(type(squares_gen)) # <class 'generator'>
# print(len(squares_gen)) # TypeError: object of type 'generator' has no len()
# next()로 하나씩 가져오기
print(next(squares_gen)) # 0 (첫 번째 값 생성)
print(next(squares_gen)) # 1 (두 번째 값 생성)
print(next(squares_gen)) # 4 (세 번째 값 생성)
# for 루프에서 사용 (자동으로 next() 호출)
for square in (i ** 2 for i in range(5)):
print(square, end=' ') # 0 1 4 9 16
print()
# 제너레이터는 한 번만 순회 가능
gen = (i for i in range(3))
print(list(gen)) # [0, 1, 2]
print(list(gen)) # [] (이미 소진됨)
메모리 효율성 비교
아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import sys
# 리스트: 모든 요소를 메모리에 저장
list_comp = [i for i in range(100000)]
print(f"리스트 크기: {sys.getsizeof(list_comp):,} bytes") # ~800,000 bytes
# 제너레이터: 객체만 저장
gen_expr = (i for i in range(100000))
print(f"제너레이터 크기: {sys.getsizeof(gen_expr):,} bytes") # ~120 bytes
# 차이: 약 6,600배!
언제 제너레이터를 사용할까?
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 1. sum, max, min 등 집계 함수와 함께
total = sum(i ** 2 for i in range(1000000)) # 메모리 효율적
maximum = max(i ** 2 for i in range(1000))
# 2. any, all 함수 (조기 종료 가능)
# 10000보다 큰 제곱수가 있는지 확인
has_large = any(i ** 2 > 10000 for i in range(1000000))
print(has_large) # True (i=101에서 조기 종료)
# 3. 파일 처리
with open('large_file.txt') as f:
# 빈 줄이 아닌 줄 수 계산
non_empty_lines = sum(1 for line in f if line.strip())
# 4. 체이닝
# 제너레이터를 여러 단계로 체이닝
numbers = range(1000000)
evens = (x for x in numbers if x % 2 == 0)
squares = (x ** 2 for x in evens)
large = (x for x in squares if x > 100)
result = sum(large) # 메모리 효율적으로 처리
제너레이터 vs 리스트 선택 가이드
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# ✅ 제너레이터 사용
# - 한 번만 순회
# - 메모리가 제한적
# - 무한 시퀀스
total = sum(i ** 2 for i in range(1000000))
# ✅ 리스트 사용
# - 여러 번 순회
# - 인덱싱 필요
# - 길이 확인 필요
squares = [i ** 2 for i in range(10)]
print(squares[5]) # 인덱싱
print(len(squares)) # 길이
print(sum(squares)) # 첫 번째 순회
print(max(squares)) # 두 번째 순회
5. 실전 예제
예제 1: CSV 데이터 파싱
CSV 문자열을 딕셔너리 리스트로 변환하는 실용적인 예제입니다. 다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# CSV 데이터 (헤더 + 데이터 행)
csv_data = "name,age,city\n철수,25,서울\n영희,30,부산\n민수,28,대전"
# 1단계: 줄 단위로 분리
lines = csv_data.strip().split('\n')
print(lines)
# ['name,age,city', '철수,25,서울', '영희,30,부산', '민수,28,대전']
# 2단계: 헤더 추출
header = lines[0].split(',')
print(header) # ['name', 'age', 'city']
# 3단계: 데이터 행을 딕셔너리로 변환
data = [
dict(zip(header, line.split(',')))
for line in lines[1:]
]
print(data)
# [{'name': '철수', 'age': '25', 'city': '서울'},
# {'name': '영희', 'age': '30', 'city': '부산'},
# {'name': '민수', 'age': '28', 'city': '대전'}]
# zip() 설명:
# header = ['name', 'age', 'city']
# line.split(',') = ['철수', '25', '서울']
# zip(header, line.split(',')) = [('name', '철수'), ('age', '25'), ('city', '서울')]
# dict(...) = {'name': '철수', 'age': '25', 'city': '서울'}
타입 변환 추가: 아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# age를 정수로 변환
data_typed = [
{
'name': parts[0],
'age': int(parts[1]), # 문자열 → 정수
'city': parts[2]
}
for line in lines[1:]
for parts in [line.split(',')] # 변수 재사용 트릭
]
print(data_typed)
# [{'name': '철수', 'age': 25, 'city': '서울'}, ...]
예제 2: 학생 성적 처리
필터링과 변환을 동시에 수행하는 예제입니다.
# 학생 데이터
students = [
{'name': '철수', 'score': 85},
{'name': '영희', 'score': 92},
{'name': '민수', 'score': 78},
{'name': '지영', 'score': 95},
{'name': '수진', 'score': 88}
]
# 필터링: 90점 이상 학생 이름만
high_scores = [
s['name'] for s in students if s['score'] >= 90
]
print(high_scores) # ['영희', '지영']
# 변환: 등급 추가
graded = [
{**s, 'grade': 'A' if s['score'] >= 90 else 'B' if s['score'] >= 80 else 'C'}
for s in students
]
print(graded)
# [{'name': '철수', 'score': 85, 'grade': 'B'},
# {'name': '영희', 'score': 92, 'grade': 'A'},
# {'name': '민수', 'score': 78, 'grade': 'C'},
# {'name': '지영', 'score': 95, 'grade': 'A'},
# {'name': '수진', 'score': 88, 'grade': 'B'}]
# {**s, 'grade': ...} 설명:
# **s: 딕셔너리 언패킹 (기존 키-값 복사)
# 'grade': ...: 새 키 추가
# 결과: 기존 딕셔너리 + 새 키
# 필터링 + 변환: 80점 이상만 등급 부여
passed = [
{**s, 'grade': 'A' if s['score'] >= 90 else 'B'}
for s in students
if s['score'] >= 80
]
print(passed)
# [{'name': '철수', 'score': 85, 'grade': 'B'},
# {'name': '영희', 'score': 92, 'grade': 'A'},
# {'name': '지영', 'score': 95, 'grade': 'A'},
# {'name': '수진', 'score': 88, 'grade': 'B'}]
예제 3: 문자열 처리
아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 문자열 리스트 정리
names = [' alice ', 'BOB', ' Charlie', 'david ']
# 공백 제거 + 소문자 변환
cleaned = [name.strip().lower() for name in names]
print(cleaned) # ['alice', 'bob', 'charlie', 'david']
# 첫 글자 대문자
capitalized = [name.strip().capitalize() for name in names]
print(capitalized) # ['Alice', 'Bob', 'Charlie', 'David']
# 길이 3 이상인 이름만
filtered = [name.strip() for name in names if len(name.strip()) >= 3]
print(filtered) # ['alice', 'BOB', 'Charlie', 'david']
예제 4: 파일 경로 처리
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import os
# 파일 목록에서 .txt 파일만 추출
files = ['data.txt', 'image.png', 'report.txt', 'video.mp4', 'notes.txt']
txt_files = [f for f in files if f.endswith('.txt')]
print(txt_files) # ['data.txt', 'report.txt', 'notes.txt']
# 확장자 제거
names_only = [os.path.splitext(f)[0] for f in txt_files]
print(names_only) # ['data', 'report', 'notes']
# 전체 경로 생성
base_path = '/home/user/documents'
full_paths = [os.path.join(base_path, f) for f in txt_files]
print(full_paths)
# ['/home/user/documents/data.txt',
# '/home/user/documents/report.txt',
# '/home/user/documents/notes.txt']
예제 5: API 응답 처리
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# API 응답 (JSON 형태)
api_response = {
'users': [
{'id': 1, 'name': 'Alice', 'active': True, 'age': 25},
{'id': 2, 'name': 'Bob', 'active': False, 'age': 30},
{'id': 3, 'name': 'Charlie', 'active': True, 'age': 35},
{'id': 4, 'name': 'David', 'active': True, 'age': 28}
]
}
# 활성 사용자 ID만 추출
active_ids = [
user['id']
for user in api_response['users']
if user['active']
]
print(active_ids) # [1, 3, 4]
# 활성 사용자 이름과 나이
active_users = [
{'name': user['name'], 'age': user['age']}
for user in api_response['users']
if user['active']
]
print(active_users)
# [{'name': 'Alice', 'age': 25},
# {'name': 'Charlie', 'age': 35},
# {'name': 'David', 'age': 28}]
# 30세 이상 활성 사용자
senior_active = [
user['name']
for user in api_response['users']
if user['active'] and user['age'] >= 30
]
print(senior_active) # ['Charlie']
6. 성능 고려사항
메모리 효율성: 리스트 vs 제너레이터
리스트 컴프리헨션은 모든 요소를 메모리에 저장하지만, 제너레이터 표현식은 필요할 때만 생성합니다. 다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import sys
# 리스트 컴프리헨션: 전체 리스트를 메모리에 생성
squares_list = [i ** 2 for i in range(1000000)]
# 제너레이터 표현식: 필요할 때만 생성 (괄호 사용)
squares_gen = (i ** 2 for i in range(1000000))
# 메모리 사용량 비교
print(f"리스트: {sys.getsizeof(squares_list):,} bytes") # ~8,000,000 bytes (8MB)
print(f"제너레이터: {sys.getsizeof(squares_gen):,} bytes") # ~120 bytes
# 제너레이터는 한 번에 하나씩만 생성
for i, square in enumerate(squares_gen):
if i >= 5:
break
print(square) # 0, 1, 4, 9, 16
언제 제너레이터를 사용할까? 다음은 python를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 1. 큰 데이터를 한 번만 순회할 때
total = sum(i ** 2 for i in range(1000000)) # 제너레이터 사용
# 2. 파일 처리
with open('large_file.txt') as f:
# 각 줄의 단어 수 계산 (메모리 효율적)
word_counts = (len(line.split()) for line in f)
total_words = sum(word_counts)
# 3. 무한 시퀀스
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 처음 10개만
first_10 = [fib for i, fib in enumerate(fibonacci()) if i < 10]
print(first_10) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
리스트가 필요한 경우: 아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 1. 여러 번 순회해야 할 때
numbers = [i for i in range(10)]
print(sum(numbers)) # 첫 번째 순회
print(max(numbers)) # 두 번째 순회 (제너레이터는 소진되어 불가)
# 2. 인덱싱이 필요할 때
squares = [i ** 2 for i in range(10)]
print(squares[5]) # 25 (제너레이터는 인덱싱 불가)
# 3. 길이를 알아야 할 때
print(len(squares)) # 10 (제너레이터는 len() 불가)
성능 벤치마크
import time
# 테스트 데이터
data = list(range(1000000))
# 1. 리스트 컴프리헨션
start = time.time()
result1 = [x * 2 for x in data if x % 2 == 0]
time1 = time.time() - start
print(f"컴프리헨션: {time1:.4f}초") # 약 0.08초
# 2. 일반 for 루프 + append
start = time.time()
result2 = []
for x in data:
if x % 2 == 0:
result2.append(x * 2)
time2 = time.time() - start
print(f"for 루프: {time2:.4f}초") # 약 0.12초
# 3. map + filter
start = time.time()
result3 = list(map(lambda x: x * 2, filter(lambda x: x % 2 == 0, data)))
time3 = time.time() - start
print(f"map+filter: {time3:.4f}초") # 약 0.15초
# 결과: 컴프리헨션이 가장 빠르고 가독성도 좋음!
중첩 컴프리헨션 주의사항
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# ❌ 너무 복잡: 3중 중첩
result = [
z
for x in range(10)
for y in range(10)
for z in range(10)
if x < y < z
]
# ✅ 개선: 함수로 분리
def is_ascending(x, y, z):
return x < y < z
result = [
z
for x in range(10)
for y in range(10)
for z in range(10)
if is_ascending(x, y, z)
]
# ✅ 더 나은 방법: itertools 사용
from itertools import combinations
result = [c[2] for c in combinations(range(10), 3)]
7. 컴프리헨션과 for 루프, 언제 무엇을 쓸까
리스트 컴프리헨션은 장바구니에 담을 물건을 한 줄로 고르는 진열처럼 읽기 쉽습니다. 분기가 많아지거나 부작용(파일 쓰기 등)이 섞이면 일반 for 루프가 의도를 드러내기 쉽습니다. 한 줄에 조건이 너무 많이 붙으면 디버깅이 어려워지므로, 그때는 루프로 쪼개는 편이 낫습니다. 다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# ✅ 컴프리헨션 사용 (간단한 변환)
squares = [x ** 2 for x in range(10)]
# ✅ 반복문 사용 (복잡한 로직)
result = []
for x in range(10):
if x % 2 == 0:
temp = x ** 2
if temp > 20:
result.append(temp)
else:
result.append(temp * 2)
# ❌ 너무 복잡한 컴프리헨션 (가독성 나쁨)
result = [
x ** 2 if x % 2 == 0 and x ** 2 > 20 else x ** 2 * 2
for x in range(10) if x % 2 == 0
]
일반적인 실수와 해결법
실수 1: 리스트 복사 문제 아래 코드는 python를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# ❌ 잘못된 방법: 같은 리스트를 여러 번 참조
matrix = [[0] * 3] * 3
matrix[0][0] = 1
print(matrix) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]] (모든 행이 변경됨!)
# ✅ 올바른 방법: 리스트 컴프리헨션으로 각 행을 독립적으로 생성
matrix = [[0] * 3 for _ in range(3)]
matrix[0][0] = 1
print(matrix) # [[1, 0, 0], [0, 0, 0], [0, 0, 0]] (첫 행만 변경)
실수 2: 불필요한 리스트 생성 아래 코드는 python를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# ❌ 비효율: 리스트를 만들고 다시 합산
total = sum([i ** 2 for i in range(1000000)]) # 8MB 메모리 사용
# ✅ 효율적: 제너레이터로 바로 합산
total = sum(i ** 2 for i in range(1000000)) # 120 bytes만 사용
실수 3: 부작용(Side Effect) 있는 함수 사용 아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# ❌ 나쁜 예: 컴프리헨션에서 부작용 발생
results = []
[results.append(x * 2) for x in range(10)] # append의 반환값(None)으로 리스트 생성
# ✅ 좋은 예: 일반 for 루프 사용
results = []
for x in range(10):
results.append(x * 2)
# ✅ 또는 컴프리헨션으로 직접 생성
results = [x * 2 for x in range(10)]
디버깅 팁
# 복잡한 컴프리헨션 디버깅
data = [1, 2, 3, 4, 5]
# 한 줄로 작성 (디버깅 어려움)
result = [x ** 2 for x in data if x % 2 == 0]
# 단계별로 분리 (디버깅 쉬움)
filtered = [x for x in data if x % 2 == 0]
print(f"필터링 결과: {filtered}") # [2, 4]
result = [x ** 2 for x in filtered]
print(f"최종 결과: {result}") # [4, 16]
실전 활용 패턴
패턴 1: 데이터 정규화 다음은 간단한 python 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 이름 정규화
raw_names = [' ALICE ', 'bob', ' Charlie ', 'DAVID']
normalized = [name.strip().title() for name in raw_names]
print(normalized) # ['Alice', 'Bob', 'Charlie', 'David']
패턴 2: 조건부 집계 아래 코드는 python를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 짝수의 합, 홀수의 합
numbers = range(1, 11)
even_sum = sum(x for x in numbers if x % 2 == 0)
odd_sum = sum(x for x in numbers if x % 2 == 1)
print(f"짝수 합: {even_sum}, 홀수 합: {odd_sum}") # 짝수 합: 30, 홀수 합: 25
패턴 3: 데이터 그룹화 다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 점수별 학생 그룹화
students = [
{'name': '철수', 'score': 85},
{'name': '영희', 'score': 92},
{'name': '민수', 'score': 85},
{'name': '지영', 'score': 92}
]
# 점수별로 이름 그룹화
from collections import defaultdict
grouped = defaultdict(list)
[grouped[s['score']].append(s['name']) for s in students]
print(dict(grouped)) # {85: ['철수', '민수'], 92: ['영희', '지영']}
# 더 나은 방법: itertools.groupby 사용 (권장)
from itertools import groupby
students_sorted = sorted(students, key=lambda s: s['score'])
grouped_better = {
score: [s['name'] for s in group]
for score, group in groupby(students_sorted, key=lambda s: s['score'])
}
print(grouped_better) # {85: ['철수', '민수'], 92: ['영희', '지영']}
8. 트러블슈팅
문제 1: “list index out of range”
아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 문제 상황
data = [[1, 2], [3, 4, 5], [6]]
# result = [row[2] for row in data] # IndexError!
# 해결 1: 조건 추가
result = [row[2] for row in data if len(row) > 2]
print(result) # [5]
# 해결 2: 기본값 사용
result = [row[2] if len(row) > 2 else None for row in data]
print(result) # [None, 5, None]
문제 2: 딕셔너리 키 중복
아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 문제: 마지막 값만 남음
items = [('a', 1), ('b', 2), ('a', 3)]
d = {k: v for k, v in items}
print(d) # {'a': 3, 'b': 2} (첫 번째 'a': 1이 덮어씌워짐)
# 해결: 값을 리스트로 수집
from collections import defaultdict
d = defaultdict(list)
[d[k].append(v) for k, v in items]
print(dict(d)) # {'a': [1, 3], 'b': [2]}
문제 3: 컴프리헨션 내부에서 예외 처리
다음은 python를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 문제: 일부 데이터가 잘못된 형식
data = ['1', '2', 'three', '4', 'five']
# ❌ 에러 발생
# result = [int(x) for x in data] # ValueError: invalid literal for int()
# ✅ 해결 1: 조건으로 필터링
result = [int(x) for x in data if x.isdigit()]
print(result) # [1, 2, 4]
# ✅ 해결 2: 함수로 예외 처리
def safe_int(x):
try:
return int(x)
except ValueError:
return None
result = [safe_int(x) for x in data]
print(result) # [1, 2, None, 4, None]
# None 제거
result_filtered = [x for x in result if x is not None]
print(result_filtered) # [1, 2, 4]
9. 상황별 요약과 코드 리뷰 체크리스트
아래 표는 7절에서 말한 “간단하면 컴프리헨션, 복잡하면 for”를 상황별로 정리한 것입니다. 예제 코드는 7절과 동일하므로, 필요하면 7절의 설명과 함께 참고하시면 됩니다.
| 상황 | 추천 방법 | 이유 |
|---|---|---|
| 간단한 변환/필터링 | 컴프리헨션 | 간결하고 빠름 |
| 복잡한 조건 로직 | 일반 for 루프 | 가독성, 디버깅 |
| 부작용(파일 쓰기, DB 저장) | 일반 for 루프 | 명확한 의도 표현 |
| 대용량 데이터 한 번 순회 | 제너레이터 | 메모리 효율 |
| 여러 번 순회 필요 | 리스트 컴프리헨션 | 재사용 가능 |
코드 리뷰 체크리스트
- 한 줄이 80자를 넘지 않는가? (PEP 8)
- 중첩이 2단계를 넘지 않는가?
- 다른 개발자가 5초 안에 이해할 수 있는가?
- 제너레이터로 대체 가능한가? (메모리 최적화)
- 예외 처리가 필요한가?
10. 연습 문제
문제 1: 기본 변환
# 1부터 20까지 중 3의 배수의 제곱을 리스트로 만드세요
# 정답: [9, 36, 81, 144, 225, 324]
문제 2: 딕셔너리 변환
# 다음 리스트를 {이름: 나이} 딕셔너리로 변환하세요
people = [('Alice', 25), ('Bob', 30), ('Charlie', 35)]
# 정답: {'Alice': 25, 'Bob': 30, 'Charlie': 35}
문제 3: 중첩 리스트 처리
# 2차원 리스트에서 짝수만 추출하여 평탄화하세요
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# 정답: [2, 4, 6, 8]
문제 4: 조건부 변환
# 온도 리스트를 "추움"(10 미만), "적당"(10-25), "더움"(25 초과)으로 분류하세요
temps = [5, 15, 30, 8, 22, 28]
# 정답: ['추움', '적당', '더움', '추움', '적당', '더움']