포스트

[Python 100일 챌린지] Day 18 - 리스트 컴프리헨션

[Python 100일 챌린지] Day 18 - 리스트 컴프리헨션

5줄 짜리 for문을 단 1줄로! Python에서 가장 우아하고 강력한 리스트 생성 방법인 리스트 컴프리헨션을 마스터해봅시다. 코드를 더 짧고 읽기 쉽게 만들어주는 Pythonic한 방법으로, 실무에서 가장 많이 사용되는 필수 문법입니다. (23분 완독 ⭐⭐)

🎯 오늘의 학습 목표

📚 사전 지식


🎯 학습 목표 1: 리스트 컴프리헨션이 무엇인지 이해하기

리스트 컴프리헨션이란?

리스트 컴프리헨션 = 리스트를 간결하게 생성하는 Pythonic한 방법

반복문과 조건문을 하나의 표현식으로 결합하여 리스트를 생성하는 Python의 강력한 기능입니다.

1
2
3
4
5
6
7
8
9
# 일반적인 방법 (5줄)
numbers = []
for i in range(5):
    numbers.append(i * 2)
print(numbers)  # [0, 2, 4, 6, 8]

# 리스트 컴프리헨션 (1줄!)
numbers = [i * 2 for i in range(5)]
print(numbers)  # [0, 2, 4, 6, 8]

왜 리스트 컴프리헨션을 사용할까?

3가지 핵심 이유

장점 설명 예시
간결성 여러 줄을 한 줄로 압축 5줄 → 1줄
가독성 의도가 명확하게 드러남 “각 요소를 제곱한다”
성능 일반 반복문보다 빠름 약 30-40% 빠름

전통적 방법 vs 컴프리헨션

예제 1: 제곱수 리스트 만들기

1
2
3
4
5
6
7
8
9
10
# ❌ 전통적 방법 (5줄)
squares = []
for i in range(1, 6):
    square = i ** 2
    squares.append(square)
print(squares)  # [1, 4, 9, 16, 25]

# ✅ 리스트 컴프리헨션 (1줄)
squares = [i ** 2 for i in range(1, 6)]
print(squares)  # [1, 4, 9, 16, 25]

예제 2: 문자열 리스트 변환

1
2
3
4
5
6
7
8
9
10
# ❌ 전통적 방법 (4줄)
fruits = ["apple", "banana", "grape"]
upper_fruits = []
for fruit in fruits:
    upper_fruits.append(fruit.upper())
print(upper_fruits)  # ['APPLE', 'BANANA', 'GRAPE']

# ✅ 리스트 컴프리헨션 (1줄)
upper_fruits = [fruit.upper() for fruit in fruits]
print(upper_fruits)  # ['APPLE', 'BANANA', 'GRAPE']

언제 사용하면 좋을까?

적합한 상황

  • 기존 리스트를 변환할 때
  • 특정 조건의 요소만 필터링할 때
  • 간단한 수학 연산을 적용할 때
  • 문자열 리스트를 처리할 때

부적합한 상황

  • 로직이 복잡할 때 (3줄 이상의 변환)
  • 부작용(side effect)이 있는 함수를 호출할 때
  • 가독성이 떨어질 때
1
2
3
4
5
6
7
# ✅ 좋은 예: 간단한 변환
doubled = [x * 2 for x in numbers]

# ❌ 나쁜 예: 복잡한 로직
result = [complex_function(x, y, z) if condition1 else other_function(x)
          for x in list1 if x > 0 for y in list2 if y < 10]
# 너무 복잡! 일반 반복문이 더 나음

🎯 학습 목표 2: 기본 문법 익히기

기본 문법 구조

핵심 패턴: [표현식 for 변수 in 반복가능객체]

1
[무엇을 만들지 for 각_요소 in 어디서_가져올지]

문법 요소 분해

요소 설명 예시
표현식 각 요소에 적용할 변환 i * 2, fruit.upper()
for 변수 반복 변수 for i, for fruit
in 반복가능객체 데이터 소스 in range(10), in fruits

기본 패턴 1: 값 그대로 복사

1
2
3
4
5
6
7
8
9
10
11
12
13
# 0부터 4까지
numbers = [i for i in range(5)]
print(numbers)  # [0, 1, 2, 3, 4]

# 리스트 복사
original = [1, 2, 3]
copied = [x for x in original]
print(copied)  # [1, 2, 3]

# 문자열 분리
text = "hello"
chars = [c for c in text]
print(chars)  # ['h', 'e', 'l', 'l', 'o']

기본 패턴 2: 값 변환

1
2
3
4
5
6
7
8
9
10
11
# 제곱
squares = [i ** 2 for i in range(5)]
print(squares)  # [0, 1, 4, 9, 16]

# 세제곱
cubes = [i ** 3 for i in range(1, 6)]
print(cubes)  # [1, 8, 27, 64, 125]

# 2배
doubled = [i * 2 for i in range(5)]
print(doubled)  # [0, 2, 4, 6, 8]

기본 패턴 3: 문자열 메서드 적용

1
2
3
4
5
6
7
8
9
10
11
12
13
fruits = ["apple", "banana", "grape"]

# 대문자 변환
upper = [fruit.upper() for fruit in fruits]
print(upper)  # ['APPLE', 'BANANA', 'GRAPE']

# 첫 글자만 대문자
capitalized = [fruit.capitalize() for fruit in fruits]
print(capitalized)  # ['Apple', 'Banana', 'Grape']

# 길이 계산
lengths = [len(fruit) for fruit in fruits]
print(lengths)  # [5, 6, 5]

기본 패턴 4: 튜플 생성

1
2
3
4
5
6
7
8
# 숫자와 제곱 쌍
pairs = [(i, i**2) for i in range(5)]
print(pairs)  # [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16)]

# 인덱스와 값 쌍
fruits = ["apple", "banana", "grape"]
indexed = [(i, fruit) for i, fruit in enumerate(fruits)]
print(indexed)  # [(0, 'apple'), (1, 'banana'), (2, 'grape')]

기본 패턴 5: 함수 적용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 내장 함수 적용
numbers = [-2, -1, 0, 1, 2]
absolutes = [abs(n) for n in numbers]
print(absolutes)  # [2, 1, 0, 1, 2]

# 사용자 정의 함수
def double(x):
    return x * 2

doubled = [double(i) for i in range(5)]
print(doubled)  # [0, 2, 4, 6, 8]

# 람다 함수
squared = [(lambda x: x**2)(i) for i in range(5)]
print(squared)  # [0, 1, 4, 9, 16]

range()와 함께 사용하기

1
2
3
4
5
6
7
8
9
10
11
# 1부터 10까지
numbers = [i for i in range(1, 11)]
print(numbers)  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 짝수만 (step=2)
evens = [i for i in range(0, 11, 2)]
print(evens)  # [0, 2, 4, 6, 8, 10]

# 역순
reversed_nums = [i for i in range(10, 0, -1)]
print(reversed_nums)  # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

🎯 학습 목표 3: 조건문 사용하기

if 조건 (필터링)

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

조건이 True인 요소만 포함됩니다.

예제 1: 짝수만 필터링

1
2
3
4
5
6
7
8
9
10
# ❌ 전통적 방법
evens = []
for i in range(10):
    if i % 2 == 0:
        evens.append(i)
print(evens)  # [0, 2, 4, 6, 8]

# ✅ 리스트 컴프리헨션
evens = [i for i in range(10) if i % 2 == 0]
print(evens)  # [0, 2, 4, 6, 8]

예제 2: 양수만 필터링

1
2
3
4
5
6
7
8
9
10
11
numbers = [-2, -1, 0, 1, 2, 3]
positives = [n for n in numbers if n > 0]
print(positives)  # [1, 2, 3]

# 음수만
negatives = [n for n in numbers if n < 0]
print(negatives)  # [-2, -1]

# 0이 아닌 값만
non_zeros = [n for n in numbers if n != 0]
print(non_zeros)  # [-2, -1, 1, 2, 3]

예제 3: 문자열 필터링

1
2
3
4
5
6
7
8
9
10
11
12
13
words = ["hi", "hello", "world", "python", "a"]

# 길이가 5 이상인 단어만
long_words = [w for w in words if len(w) >= 5]
print(long_words)  # ['hello', 'world', 'python']

# 'o'가 포함된 단어만
with_o = [w for w in words if 'o' in w]
print(with_o)  # ['hello', 'world', 'python']

# 대문자로 시작하지 않는 단어만
lowercase_start = [w for w in words if not w[0].isupper()]
print(lowercase_start)  # 모든 단어 (다 소문자 시작)

if-else (값 변경)

패턴: [A if 조건 else B for 변수 in 반복가능객체]

조건에 따라 다른 값을 생성합니다.

1
2
3
4
5
6
7
8
# 짝수는 "짝", 홀수는 "홀"
result = ["" if i % 2 == 0 else "" for i in range(5)]
print(result)  # ['짝', '홀', '짝', '홀', '짝']

# 양수는 그대로, 음수/0은 0으로
numbers = [-2, -1, 0, 1, 2]
result = [n if n > 0 else 0 for n in numbers]
print(result)  # [0, 0, 0, 1, 2]

복합 조건

1
2
3
4
5
6
7
8
9
10
11
# 여러 조건 결합 (and)
numbers = [i for i in range(20) if i % 2 == 0 if i % 3 == 0]
print(numbers)  # [0, 6, 12, 18] (2와 3의 공배수)

# 또는 (하나의 if문으로)
numbers = [i for i in range(20) if i % 2 == 0 and i % 3 == 0]
print(numbers)  # [0, 6, 12, 18]

# or 조건
numbers = [i for i in range(10) if i % 2 == 0 or i % 3 == 0]
print(numbers)  # [0, 2, 3, 4, 6, 8, 9]

🎯 학습 목표 4: 중첩 컴프리헨션 이해하기

중첩 반복문 (다중 for)

패턴: [표현식 for 변수1 in 반복가능객체1 for 변수2 in 반복가능객체2]

1
2
3
4
5
6
7
8
# 좌표 생성 (3x3)
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)]

# 구구단 (2단~9단)
multiplication = [f"{i}x{j}={i*j}" for i in range(2, 10) for j in range(1, 10)]
print(multiplication[:5])  # ['2x1=2', '2x2=4', '2x3=6', '2x4=8', '2x5=10']

2차원 리스트 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 3x3 영행렬
matrix = [[0 for _ in range(3)] for _ in range(3)]
print(matrix)
# [[0, 0, 0], [0, 0, 0], [0, 0, 0]]

# 곱셈표 (3x3)
table = [[i*j for j in range(1, 4)] for i in range(1, 4)]
print(table)
# [[1, 2, 3], [2, 4, 6], [3, 6, 9]]

# 5x5 단위 행렬
identity = [[1 if i==j else 0 for j in range(5)] for i in range(5)]
print(identity)
# [[1, 0, 0, 0, 0],
#  [0, 1, 0, 0, 0],
#  [0, 0, 1, 0, 0],
#  [0, 0, 0, 1, 0],
#  [0, 0, 0, 0, 1]]

2차원 리스트 평탄화 (Flatten)

1
2
3
4
5
6
7
8
9
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# 평탄화
flat = [num for row in matrix for num in row]
print(flat)  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 조건 추가: 짝수만
even_flat = [num for row in matrix for num in row if num % 2 == 0]
print(even_flat)  # [2, 4, 6, 8]

💡 실전 팁 & 주의사항

Tip 1: if와 if-else 위치 차이

1
2
3
4
5
6
7
8
9
10
11
12
# ❌ 헷갈리기 쉬운 부분
# if는 맨 뒤 (필터링)
evens = [i for i in range(10) if i % 2 == 0]

# if-else는 맨 앞 (값 변경)
result = ["" if i % 2 == 0 else "" for i in range(5)]

# ❌ 이렇게 쓰면 에러!
# result = [i for i in range(10) if i % 2 == 0 else 0]  # SyntaxError!

# ✅ 올바른 방법
result = [i if i % 2 == 0 else 0 for i in range(10)]

Tip 2: 너무 복잡하면 분리하기

1
2
3
4
5
6
7
8
# ❌ 가독성이 떨어지는 예
result = [[i*j for j in range(1, 10) if j % 2 == 0]
          for i in range(1, 10) if i % 2 == 1]

# ✅ 단계별로 분리
odd_numbers = [i for i in range(1, 10) if i % 2 == 1]
even_numbers = [j for j in range(1, 10) if j % 2 == 0]
result = [[i*j for j in even_numbers] for i in odd_numbers]

Tip 3: 빈 리스트 주의

1
2
3
4
5
6
7
words = []

# 빈 리스트에 컴프리헨션 적용하면 빈 리스트 반환
upper_words = [w.upper() for w in words]
print(upper_words)  # []

# 에러는 안 나지만 결과가 없음

Tip 4: 언더스코어(_) 활용

1
2
3
4
5
6
# 변수를 사용하지 않을 때는 _
zeros = [0 for _ in range(10)]
print(zeros)  # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

# 2차원 리스트
matrix = [[0 for _ in range(3)] for _ in range(3)]

Tip 5: map()과 filter() 대신 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
numbers = [1, 2, 3, 4, 5]

# ❌ map() 사용 (함수형 프로그래밍)
doubled = list(map(lambda x: x * 2, numbers))

# ✅ 리스트 컴프리헨션 (Pythonic)
doubled = [x * 2 for x in numbers]

# ❌ filter() 사용
evens = list(filter(lambda x: x % 2 == 0, numbers))

# ✅ 리스트 컴프리헨션
evens = [x for x in numbers if x % 2 == 0]

Tip 6: 성능 고려사항

1
2
3
4
5
6
# 작은 리스트: 컴프리헨션이 빠름
small = [i**2 for i in range(100)]

# 매우 큰 리스트: 메모리 고려 필요
# Generator expression 사용 고려
large = (i**2 for i in range(1000000))  # 괄호 () 사용

🧪 연습 문제

문제 1: 학생 성적 데이터 처리 시스템

과제: 학생들의 점수 리스트를 여러 조건으로 필터링하고 변환하는 프로그램을 작성하세요. 리스트 컴프리헨션의 다양한 패턴을 활용합니다.

초기 데이터:

1
2
3
4
5
6
7
8
9
10
students = [
    {"name": "홍길동", "score": 85},
    {"name": "김철수", "score": 92},
    {"name": "이영희", "score": 78},
    {"name": "박민수", "score": 95},
    {"name": "정수진", "score": 67},
    {"name": "최지훈", "score": 88},
    {"name": "한미영", "score": 73},
    {"name": "윤성호", "score": 90}
]

요구사항:

  1. 80점 이상인 학생들의 이름만 추출하기
  2. 모든 학생의 점수를 10% 증가시킨 새 리스트 만들기 (소수점 첫째 자리)
  3. 학점 판정하기:
    • 90점 이상: “A”
    • 80점 이상: “B”
    • 70점 이상: “C”
    • 나머지: “D”
  4. 합격자(70점 이상)와 불합격자(70점 미만)로 분류하기
  5. 각 학생의 이름과 학점을 튜플 리스트로 만들기: [("이름", "학점"), ...]
  6. 통계: 평균, 최고점, 최저점, 합격률 계산하기
💡 힌트
  • 필터링: [변수 for 변수 in 리스트 if 조건]
  • 변환: [표현식 for 변수 in 리스트]
  • if-else: [A if 조건 else B for 변수 in 리스트]
  • 딕셔너리 접근: student["name"], student["score"]
  • 학점 판정: if-elif-else 체이닝
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
students = [
    {"name": "홍길동", "score": 85},
    {"name": "김철수", "score": 92},
    {"name": "이영희", "score": 78},
    {"name": "박민수", "score": 95},
    {"name": "정수진", "score": 67},
    {"name": "최지훈", "score": 88},
    {"name": "한미영", "score": 73},
    {"name": "윤성호", "score": 90}
]

print("===== 학생 성적 데이터 처리 =====\n")

# 1. 80점 이상인 학생들의 이름 추출
high_scorers = [s["name"] for s in students if s["score"] >= 80]
print(f"1. 80점 이상 학생들:")
print(f"   {', '.join(high_scorers)}")
print(f"{len(high_scorers)}\n")

# 2. 모든 학생의 점수를 10% 증가 (보너스 점수)
bonus_scores = [round(s["score"] * 1.1, 1) for s in students]
print(f"2. 보너스 점수 (10% 증가):")
for i, student in enumerate(students):
    print(f"   {student['name']}: {student['score']}점 → {bonus_scores[i]}")
print()

# 3. 학점 판정 (원본 점수 기준)
grades = [
    "A" if s["score"] >= 90 else
    "B" if s["score"] >= 80 else
    "C" if s["score"] >= 70 else
    "D"
    for s in students
]
print(f"3. 학점 판정:")
for i, student in enumerate(students):
    print(f"   {student['name']}: {student['score']}점 → {grades[i]}학점")
print()

# 4. 합격자(70점 이상)와 불합격자 분류
passed = [s["name"] for s in students if s["score"] >= 70]
failed = [s["name"] for s in students if s["score"] < 70]
print(f"4. 합격/불합격 분류:")
print(f"   합격 ({len(passed)}명): {', '.join(passed)}")
print(f"   불합격 ({len(failed)}명): {', '.join(failed) if failed else '없음'}")
print()

# 5. 이름과 학점 튜플 리스트
name_grade_pairs = [
    (s["name"],
     "A" if s["score"] >= 90 else
     "B" if s["score"] >= 80 else
     "C" if s["score"] >= 70 else
     "D")
    for s in students
]
print(f"5. 이름-학점 튜플 리스트:")
for name, grade in name_grade_pairs:
    print(f"   {name}: {grade}")
print()

# 6. 통계 계산
scores = [s["score"] for s in students]
average = sum(scores) / len(scores)
max_score = max(scores)
min_score = min(scores)
pass_rate = len(passed) / len(students) * 100

print(f"6. 통계:")
print(f"   평균 점수: {average:.1f}")
print(f"   최고 점수: {max_score}")
print(f"   최저 점수: {min_score}")
print(f"   합격률: {pass_rate:.1f}%")

# 추가: 학점별 인원 수
grade_a = len([g for g in grades if g == "A"])
grade_b = len([g for g in grades if g == "B"])
grade_c = len([g for g in grades if g == "C"])
grade_d = len([g for g in grades if g == "D"])

print(f"\n7. 학점별 분포:")
print(f"   A학점: {grade_a}")
print(f"   B학점: {grade_b}")
print(f"   C학점: {grade_c}")
print(f"   D학점: {grade_d}")

# 추가: 상위 3명
top_3 = sorted(students, key=lambda x: x["score"], reverse=True)[:3]
print(f"\n8. 상위 3명:")
for i, student in enumerate(top_3, 1):
    print(f"   {i}등: {student['name']} ({student['score']}점)")

출력:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
===== 학생 성적 데이터 처리 =====

1. 80점 이상 학생들:
   홍길동, 김철수, 박민수, 최지훈, 윤성호
   총 5명

2. 보너스 점수 (10% 증가):
   홍길동: 85점 → 93.5점
   김철수: 92점 → 101.2점
   이영희: 78점 → 85.8점
   박민수: 95점 → 104.5점
   정수진: 67점 → 73.7점
   최지훈: 88점 → 96.8점
   한미영: 73점 → 80.3점
   윤성호: 90점 → 99.0점

3. 학점 판정:
   홍길동: 85점 → B학점
   김철수: 92점 → A학점
   이영희: 78점 → C학점
   박민수: 95점 → A학점
   정수진: 67점 → D학점
   최지훈: 88점 → B학점
   한미영: 73점 → C학점
   윤성호: 90점 → A학점

4. 합격/불합격 분류:
   합격 (7명): 홍길동, 김철수, 이영희, 박민수, 최지훈, 한미영, 윤성호
   불합격 (1명): 정수진

5. 이름-학점 튜플 리스트:
   홍길동: B
   김철수: A
   이영희: C
   박민수: A
   정수진: D
   최지훈: B
   한미영: C
   윤성호: A

6. 통계:
   평균 점수: 83.5점
   최고 점수: 95점
   최저 점수: 67점
   합격률: 87.5%

7. 학점별 분포:
   A학점: 3명
   B학점: 2명
   C학점: 2명
   D학점: 1명

8. 상위 3명:
   1등: 박민수 (95점)
   2등: 김철수 (92점)
   3등: 윤성호 (90점)

문제 2: 데이터 변환 및 중첩 리스트 컴프리헨션

과제: 중첩 리스트를 활용하여 2차원 데이터를 생성하고 변환하는 프로그램을 작성하세요.

초기 데이터:

1
2
3
4
5
6
# 3개 반의 학생 점수 (반별 5명)
classes = [
    [85, 92, 78, 95, 88],  # 1반
    [90, 85, 88, 92, 87],  # 2반
    [78, 82, 85, 79, 91]   # 3반
]

요구사항:

  1. 각 반의 평균 점수를 계산하여 리스트로 만들기
  2. 모든 반의 모든 점수를 하나의 1차원 리스트로 평탄화하기
  3. 80점 이상인 학생 수를 반별로 계산하기
  4. 구구단 2~5단을 2차원 리스트로 생성하기 (중첩 컴프리헨션)
  5. 좌표 평면의 격자점 생성하기: x는 0~2, y는 0~2 → [(0,0), (0,1), (0,2), …]
  6. FizzBuzz 문제를 1부터 30까지 리스트로 만들기:
    • 15의 배수: “FizzBuzz”
    • 3의 배수: “Fizz”
    • 5의 배수: “Buzz”
    • 나머지: 숫자 그대로
💡 힌트
  • 평균: sum(리스트) / len(리스트)
  • 평탄화: [item for sublist in 리스트 for item in sublist]
  • 중첩 컴프리헨션: [[내부식 for j in ...] for i in ...]
  • 튜플 생성: [(x, y) for x in ... for y in ...]
  • if-elif-else 체이닝: 조건을 앞에서부터 검사
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
classes = [
    [85, 92, 78, 95, 88],  # 1반
    [90, 85, 88, 92, 87],  # 2반
    [78, 82, 85, 79, 91]   # 3반
]

print("===== 데이터 변환 및 중첩 리스트 컴프리헨션 =====\n")

# 1. 각 반의 평균 점수
averages = [sum(class_scores) / len(class_scores) for class_scores in classes]
print("1. 반별 평균 점수:")
for i, avg in enumerate(averages, 1):
    print(f"   {i}반: {avg:.1f}")
print()

# 2. 모든 점수를 하나의 1차원 리스트로 평탄화
all_scores = [score for class_scores in classes for score in class_scores]
print(f"2. 평탄화된 전체 점수:")
print(f"   {all_scores}")
print(f"{len(all_scores)}명의 점수")
print()

# 3. 80점 이상인 학생 수 (반별)
high_scorers_count = [len([s for s in class_scores if s >= 80]) for class_scores in classes]
print("3. 반별 80점 이상 학생 수:")
for i, count in enumerate(high_scorers_count, 1):
    print(f"   {i}반: {count}명 / {len(classes[i-1])}")
print()

# 4. 구구단 2~5단 (2차원 리스트)
gugudan = [[i * j for j in range(1, 10)] for i in range(2, 6)]
print("4. 구구단 2~5단:")
for i, dan in enumerate(gugudan, start=2):
    print(f"   {i}단: {dan}")
print()

# 구구단 문자열 버전 (보기 좋게)
gugudan_str = [[f"{i}×{j}={i*j}" for j in range(1, 10)] for i in range(2, 6)]
print("   [문자열 버전]")
for i, dan in enumerate(gugudan_str, start=2):
    print(f"   {i}단: {', '.join(dan[:3])} ...")

print()

# 5. 좌표 평면의 격자점 생성 (0~2 × 0~2)
coordinates = [(x, y) for x in range(3) for y in range(3)]
print("5. 좌표 평면 격자점 (0~2 × 0~2):")
print(f"   {coordinates}")
print(f"{len(coordinates)}개의 좌표")

# 3×3 격자 시각화
print("\n   [격자 시각화]")
for y in range(2, -1, -1):
    row_coords = [(x, y) for x in range(3)]
    print(f"   y={y}: {row_coords}")
print()

# 6. FizzBuzz (1~30)
fizzbuzz = [
    "FizzBuzz" if i % 15 == 0 else
    "Fizz" if i % 3 == 0 else
    "Buzz" if i % 5 == 0 else
    i
    for i in range(1, 31)
]
print("6. FizzBuzz (1~30):")
print(f"   {fizzbuzz}")

# FizzBuzz 통계
fizz_count = len([x for x in fizzbuzz if x == "Fizz"])
buzz_count = len([x for x in fizzbuzz if x == "Buzz"])
fizzbuzz_count = len([x for x in fizzbuzz if x == "FizzBuzz"])
number_count = len([x for x in fizzbuzz if isinstance(x, int)])

print(f"\n   [통계]")
print(f"   Fizz: {fizz_count}")
print(f"   Buzz: {buzz_count}")
print(f"   FizzBuzz: {fizzbuzz_count}")
print(f"   숫자: {number_count}")

# 추가: 전체 통계
print(f"\n7. 전체 학생 통계:")
total_avg = sum(all_scores) / len(all_scores)
total_max = max(all_scores)
total_min = min(all_scores)
total_high = len([s for s in all_scores if s >= 80])

print(f"   전체 평균: {total_avg:.1f}")
print(f"   최고 점수: {total_max}")
print(f"   최저 점수: {total_min}")
print(f"   80점 이상: {total_high}명 ({total_high/len(all_scores)*100:.1f}%)")

출력:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
===== 데이터 변환 및 중첩 리스트 컴프리헨션 =====

1. 반별 평균 점수:
   1반: 87.6점
   2반: 88.4점
   3반: 83.0점

2. 평탄화된 전체 점수:
   [85, 92, 78, 95, 88, 90, 85, 88, 92, 87, 78, 82, 85, 79, 91]
   총 15명의 점수

3. 반별 80점 이상 학생 수:
   1반: 4명 / 5명
   2반: 5명 / 5명
   3반: 3명 / 5명

4. 구구단 2~5단:
   2단: [2, 4, 6, 8, 10, 12, 14, 16, 18]
   3단: [3, 6, 9, 12, 15, 18, 21, 24, 27]
   4단: [4, 8, 12, 16, 20, 24, 28, 32, 36]
   5단: [5, 10, 15, 20, 25, 30, 35, 40, 45]

   [문자열 버전]
   2단: 2×1=2, 2×2=4, 2×3=6 ...
   3단: 3×1=3, 3×2=6, 3×3=9 ...
   4단: 4×1=4, 4×2=8, 4×3=12 ...
   5단: 5×1=5, 5×2=10, 5×3=15 ...

5. 좌표 평면 격자점 (0~2 × 0~2):
   [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
   총 9개의 좌표

   [격자 시각화]
   y=2: [(0, 2), (1, 2), (2, 2)]
   y=1: [(0, 1), (1, 1), (2, 1)]
   y=0: [(0, 0), (1, 0), (2, 0)]

6. FizzBuzz (1~30):
   [1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz', 11, 'Fizz', 13, 14, 'FizzBuzz', 16, 17, 'Fizz', 19, 'Buzz', 'Fizz', 22, 23, 'Fizz', 'Buzz', 26, 'Fizz', 28, 29, 'FizzBuzz']

   [통계]
   Fizz: 8개
   Buzz: 4개
   FizzBuzz: 2개
   숫자: 16개

7. 전체 학생 통계:
   전체 평균: 86.3점
   최고 점수: 95점
   최저 점수: 78점
   80점 이상: 12명 (80.0%)

🎯 연습 문제 핵심 포인트

문제 1에서 배운 것:

  • 조건 필터링: [x for x in 리스트 if 조건]
  • 값 변환: [표현식 for x in 리스트]
  • if-else 체이닝: 복잡한 조건 판정 (학점)
  • 딕셔너리 리스트 처리: 실제 데이터 구조 다루기
  • 통계 계산: 평균, 최고/최저, 비율

문제 2에서 배운 것:

  • 중첩 리스트 컴프리헨션: 2차원 데이터 생성
  • 평탄화: 다차원 → 1차원 변환
  • 좌표 생성: 데카르트 곱(Cartesian product)
  • FizzBuzz: if-elif-else 체이닝의 실전 활용
  • 다중 변환: 여러 방식으로 데이터 가공

📝 오늘 배운 내용 정리

  1. 리스트 컴프리헨션: 반복문과 조건문을 하나의 표현식으로 결합하여 리스트 생성
  2. 기본 문법: [표현식 for 변수 in 반복가능객체]
  3. if 조건: [표현식 for 변수 in 반복가능객체 if 조건] - 필터링
  4. if-else: [A if 조건 else B for 변수 in 반복가능객체] - 값 변경
  5. 중첩 컴프리헨션: 2차원 리스트 생성, 평탄화
  6. 장점: 간결성, 가독성, 성능 (30-40% 빠름)

🔗 관련 자료


📚 이전 학습

Day 17: 문자열 메서드 활용 ⭐⭐

어제는 문자열 메서드로 텍스트를 다루는 방법을 배웠습니다!

📚 다음 학습

Day 19: 딕셔너리 컴프리헨션 ⭐⭐

내일은 딕셔너리를 간결하게 만드는 방법을 배웁니다!


“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀

Day 18/100 Phase 2: 자료형 마스터하기 #100DaysOfPython
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.