학생 이름, 나이, 점수가 각각 다른 리스트에 저장되어 있어요.
1
2
3
| names = ["철수", "영희", "민수"]
ages = [20, 19, 21]
scores = [85, 92, 88]
|
이걸 “철수(20세): 85점” 형태로 출력하려면 어떻게 해야 할까요? 🤔
Day 9에서 배운 방법은 인덱스를 사용하는 거였죠:
1
2
| for i in range(len(names)):
print(f"{names[i]}({ages[i]}세): {scores[i]}점")
|
하지만 더 간단하고 읽기 쉬운 방법이 있습니다:
1
2
| for name, age, score in zip(names, ages, scores):
print(f"{name}({age}세): {score}점")
|
인덱스 없이 직접 값을 사용하니 훨씬 깔끔하죠? 이게 바로 for문을 스마트하게 쓰는 패턴입니다.
오늘은 Day 9의 기초를 넘어, for문을 더 똑똑하게 활용하는 방법을 배웁니다. zip()으로 여러 리스트 묶기, for-else로 검색 결과 처리, 딕셔너리 순회 마스터까지!
(40분 완독 ⭐⭐⭐)
🎯 오늘의 학습 목표
- zip()으로 여러 리스트 동시 순회하기
- 중첩 for문 마스터 - 2D 리스트 탐색
- for-else와 pass 실전 활용
- 딕셔너리 순회 완벽 가이드
📚 사전 지식
💡 Day 9에서 배운 for문 기초를 실전 레벨로 끌어올립니다!
🎯 학습 목표 1: zip()으로 여러 리스트 동시 순회하기
문제 상황: 여러 리스트를 함께 사용하고 싶다면?
1
2
3
4
5
6
7
8
| # 학생 정보가 3개 리스트에 분리되어 있음
names = ["철수", "영희", "민수"]
ages = [20, 19, 21]
scores = [85, 92, 88]
# ❌ 비효율적인 방법: 인덱스 사용
for i in range(len(names)):
print(f"{names[i]}({ages[i]}세): {scores[i]}점")
|
문제점:
len(names) 매번 호출 - 인덱스
[i] 반복 사용으로 가독성 떨어짐 - 리스트 길이가 다르면 에러 발생 가능
✅ zip() 사용: 우아한 해결책
한 줄 설명: zip() = 여러 리스트를 나란히 묶어주는 함수
1
2
3
4
5
6
| names = ["철수", "영희", "민수"]
ages = [20, 19, 21]
scores = [85, 92, 88]
for name, age, score in zip(names, ages, scores):
print(f"{name}({age}세): {score}점")
|
출력:
1
2
3
| 철수(20세): 85점
영희(19세): 92점
민수(21세): 88점
|
zip() 동작 원리
1
2
3
4
5
6
7
8
9
10
11
| names = ["철수", "영희"]
ages = [20, 19]
# zip()의 결과는 튜플로 묶여있음
result = list(zip(names, ages))
print(result)
# [('철수', 20), ('영희', 19)]
# for문에서는 자동으로 언패킹
for name, age in zip(names, ages):
print(f"{name}: {age}세")
|
길이가 다른 리스트는 어떻게 될까?
1
2
3
4
5
| names = ["철수", "영희", "민수"]
ages = [20, 19] # 하나 적음!
for name, age in zip(names, ages):
print(f"{name}: {age}세")
|
출력:
💡 zip()의 특징: 가장 짧은 리스트에 맞춰서 끝남 (민수는 출력 안 됨)
실전 예제 1: 학생 정보 정리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| names = ["김철수", "이영희", "박민수"]
korean = [85, 92, 78]
english = [90, 88, 95]
math = [78, 95, 85]
print("="*50)
print("학생별 성적 리포트")
print("="*50)
for name, kor, eng, mat in zip(names, korean, english, math):
total = kor + eng + mat
average = total / 3
print(f"\n📝 {name}")
print(f" 국어: {kor}점 | 영어: {eng}점 | 수학: {mat}점")
print(f" 총점: {total}점 | 평균: {average:.1f}점")
|
출력:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| ==================================================
학생별 성적 리포트
==================================================
📝 김철수
국어: 85점 | 영어: 90점 | 수학: 78점
총점: 253점 | 평균: 84.3점
📝 이영희
국어: 92점 | 영어: 88점 | 수학: 95점
총점: 275점 | 평균: 91.7점
📝 박민수
국어: 78점 | 영어: 95점 | 수학: 85점
총점: 258점 | 평균: 86.0점
|
실전 예제 2: 딕셔너리 만들기
1
2
3
4
5
6
7
| keys = ["name", "age", "city"]
values = ["철수", 20, "서울"]
# zip()으로 딕셔너리 생성
student = dict(zip(keys, values))
print(student)
# {'name': '철수', 'age': 20, 'city': '서울'}
|
실전 예제 3: 두 리스트 비교
1
2
3
4
5
6
7
| expected = [1, 2, 3, 4, 5]
actual = [1, 2, 9, 4, 5]
print("테스트 결과:")
for i, (exp, act) in enumerate(zip(expected, actual), start=1):
status = "✅ PASS" if exp == act else "❌ FAIL"
print(f" 테스트 {i}: {status} (기대값: {exp}, 실제값: {act})")
|
출력:
1
2
3
4
5
6
| 테스트 결과:
테스트 1: ✅ PASS (기대값: 1, 실제값: 1)
테스트 2: ✅ PASS (기대값: 2, 실제값: 2)
테스트 3: ❌ FAIL (기대값: 3, 실제값: 9)
테스트 4: ✅ PASS (기대값: 4, 실제값: 4)
테스트 5: ✅ PASS (기대값: 5, 실제값: 5)
|
🎯 학습 목표 2: 중첩 for문 마스터 - 2D 리스트 탐색
2D 리스트 (2차원 리스트) 순회
한 줄 설명: 2D 리스트 = 리스트 안에 리스트 (엑셀 표 같은 구조)
1
2
3
4
5
6
7
8
9
10
11
12
| # 3명 학생의 3과목 점수 (3 x 3 표)
scores = [
[85, 90, 78], # 철수: 국어, 영어, 수학
[92, 88, 95], # 영희
[78, 95, 85] # 민수
]
# 모든 점수 출력
for row in scores:
for score in row:
print(score, end=" ")
print() # 줄바꿈
|
출력:
1
2
3
| 85 90 78
92 88 95
78 95 85
|
enumerate()와 함께 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| scores = [
[85, 90, 78],
[92, 88, 95],
[78, 95, 85]
]
names = ["철수", "영희", "민수"]
subjects = ["국어", "영어", "수학"]
# 표 형식으로 출력
print(" ", end="")
for subject in subjects:
print(f"{subject:>6}", end="")
print()
print("="*25)
for student_idx, row in enumerate(scores):
print(f"{names[student_idx]:>4}", end="")
for score in row:
print(f"{score:>6}", end="")
print()
|
출력:
1
2
3
4
5
| 국어 영어 수학
=========================
철수 85 90 78
영희 92 88 95
민수 78 95 85
|
실전 예제: 특정 값 찾기 (2D 리스트에서 최댓값 위치)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| scores = [
[85, 90, 78],
[92, 88, 95],
[78, 95, 85]
]
max_score = 0
max_row = 0
max_col = 0
for row_idx, row in enumerate(scores):
for col_idx, score in enumerate(row):
if score > max_score:
max_score = score
max_row = row_idx
max_col = col_idx
names = ["철수", "영희", "민수"]
subjects = ["국어", "영어", "수학"]
print(f"최고 점수: {max_score}점")
print(f"학생: {names[max_row]}")
print(f"과목: {subjects[max_col]}")
|
출력:
1
2
3
| 최고 점수: 95점
학생: 영희
과목: 수학
|
실전 예제: 성적표에서 통계 구하기
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
| # 학생별 과목 점수 (2D 리스트)
scores = [
[85, 90, 78], # 철수: 국어, 영어, 수학
[92, 88, 95], # 영희
[78, 95, 85] # 민수
]
names = ["철수", "영희", "민수"]
subjects = ["국어", "영어", "수학"]
print("="*50)
print("📊 학생별 성적 분석")
print("="*50)
# 1. 학생별 평균 계산
print("\n1️⃣ 학생별 평균:")
for i, student_scores in enumerate(scores):
total = sum(student_scores)
average = total / len(student_scores)
print(f" {names[i]}: {average:.1f}점")
# 2. 과목별 평균 계산 (세로로 순회)
print("\n2️⃣ 과목별 평균:")
for subject_idx in range(len(subjects)):
total = 0
for student_scores in scores:
total += student_scores[subject_idx]
average = total / len(scores)
print(f" {subjects[subject_idx]}: {average:.1f}점")
# 3. 최고점과 최저점 찾기
max_score = 0
min_score = 100
for row in scores:
for score in row:
if score > max_score:
max_score = score
if score < min_score:
min_score = score
print(f"\n3️⃣ 전체 최고점: {max_score}점")
print(f"3️⃣ 전체 최저점: {min_score}점")
|
출력:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| ==================================================
📊 학생별 성적 분석
==================================================
1️⃣ 학생별 평균:
철수: 84.3점
영희: 91.7점
민수: 86.0점
2️⃣ 과목별 평균:
국어: 85.0점
영어: 91.0점
수학: 86.0점
3️⃣ 전체 최고점: 95점
3️⃣ 전체 최저점: 78점
|
💡 2D 리스트 활용 팁: 가로(학생별), 세로(과목별) 모두 계산 가능!
🎯 학습 목표 3: for-else와 pass 실전 활용
for-else: 검색 결과를 깔끔하게 처리
한 줄 설명: for문이 break 없이 정상 종료되면 else 블록 실행
💡 “찾았다/못 찾았다”를 판단할 때 가장 유용합니다!
1
2
3
4
5
6
7
8
| # 기본 구조
for 항목 in 리스트:
# 반복 실행
if 조건:
break # break 만나면 else 건너뜀
else:
# break 없이 끝나면 여기 실행
print("모두 확인 완료!")
|
실전 예제 1: 검색 성공/실패 판단
1
2
3
4
5
6
7
8
9
10
11
12
13
| # ❌ break 없이 구현 (복잡함)
numbers = [1, 3, 5, 7, 9]
target = 4
found = False
for num in numbers:
if num == target:
print(f"✅ {target}를 찾았습니다!")
found = True
break
if not found:
print(f"❌ {target}를 찾지 못했습니다.")
|
1
2
3
4
5
6
7
8
9
10
| # ✅ for-else 사용 (깔끔함)
numbers = [1, 3, 5, 7, 9]
target = 4
for num in numbers:
if num == target:
print(f"✅ {target}를 찾았습니다!")
break
else:
print(f"❌ {target}를 찾지 못했습니다.")
|
출력 (target=4):
출력 (target=5):
실전 예제 2: 로그인 시도 제한
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| def login_system():
"""3번까지 비밀번호 입력 기회"""
correct_password = "python123"
max_attempts = 3
for attempt in range(1, max_attempts + 1):
password = input(f"비밀번호 입력 (시도 {attempt}/{max_attempts}): ")
if password == correct_password:
print("✅ 로그인 성공!")
break
else:
print("❌ 로그인 실패! 계정이 잠겼습니다.")
# login_system() # 실제로 실행해보세요!
|
pass: 코드 구조 먼저 만들기
한 줄 설명: pass = “아무것도 안 함” (코드 자리 표시용)
Python은 빈 블록을 허용하지 않습니다. 그래서 구조만 먼저 만들 때 pass를 사용합니다.
1
2
3
4
5
6
7
8
9
10
11
12
| # ❌ 에러 발생!
for item in items:
if item == "특별한 케이스":
# TODO: 나중에 구현
# SyntaxError!
# ✅ pass 사용
for item in items:
if item == "특별한 케이스":
pass # TODO: 나중에 구현
else:
print(f"처리: {item}")
|
pass vs continue vs break 비교
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # pass: 아무것도 안 함
for i in range(5):
if i == 2:
pass
print(i)
# 출력: 0 1 2 3 4 (모두 출력)
# continue: 현재 반복 건너뛰기
for i in range(5):
if i == 2:
continue
print(i)
# 출력: 0 1 3 4 (2 제외)
# break: 루프 완전 중단
for i in range(5):
if i == 2:
break
print(i)
# 출력: 0 1 (2부터 중단)
|
🎯 학습 목표 4: 딕셔너리 순회 완벽 가이드
패턴 1: 키만 순회 (기본)
1
2
3
4
| scores = {"철수": 85, "영희": 92, "민수": 88}
for name in scores:
print(name)
|
출력:
패턴 2: 값만 순회 (.values())
1
2
3
4
| scores = {"철수": 85, "영희": 92, "민수": 88}
for score in scores.values():
print(f"{score}점")
|
출력:
패턴 3: 키-값 쌍 순회 (.items()) ⭐ 가장 많이 사용
1
2
3
4
| scores = {"철수": 85, "영희": 92, "민수": 88}
for name, score in scores.items():
print(f"{name}: {score}점")
|
출력:
1
2
3
| 철수: 85점
영희: 92점
민수: 88점
|
패턴 4: 인덱스와 함께 순회
1
2
3
4
| scores = {"철수": 85, "영희": 92, "민수": 88}
for idx, (name, score) in enumerate(scores.items(), start=1):
print(f"{idx}등: {name} ({score}점)")
|
출력:
1
2
3
| 1등: 철수 (85점)
2등: 영희 (92점)
3등: 민수 (88점)
|
패턴 5: 정렬하면서 순회
1
2
3
4
5
6
7
8
9
10
11
| scores = {"철수": 85, "영희": 92, "민수": 88}
# 키 기준 정렬
print("이름순:")
for name in sorted(scores.keys()):
print(f" {name}: {scores[name]}점")
print("\n점수순 (높은 순):")
# 값 기준 정렬 (lambda는 Phase 3 후반에 배움)
for name, score in sorted(scores.items(), key=lambda x: x[1], reverse=True):
print(f" {name}: {score}점")
|
출력:
1
2
3
4
5
6
7
8
9
| 이름순:
민수: 88점
영희: 92점
철수: 85점
점수순 (높은 순):
영희: 92점
민수: 88점
철수: 85점
|
실전 예제: 딕셔너리 필터링
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| scores = {
"철수": 85,
"영희": 92,
"민수": 78,
"지영": 95,
"동수": 88
}
# 90점 이상만 추출
high_scores = {}
for name, score in scores.items():
if score >= 90:
high_scores[name] = score
print("90점 이상 우수 학생:")
for name, score in high_scores.items():
print(f" 🏆 {name}: {score}점")
|
출력:
1
2
3
| 90점 이상 우수 학생:
🏆 영희: 92점
🏆 지영: 95점
|
💡 실전 팁 & 주의사항
1. enumerate() vs range(len())
1
2
3
4
5
6
7
8
9
| items = ["사과", "바나나", "포도"]
# ❌ 비추천
for i in range(len(items)):
print(f"{i}: {items[i]}")
# ✅ 추천 (더 파이썬다움)
for i, item in enumerate(items):
print(f"{i}: {item}")
|
2. zip()의 길이 불일치 주의
1
2
3
4
5
6
7
8
| names = ["철수", "영희", "민수"]
scores = [85, 92] # 하나 부족!
# 민수의 점수가 빠짐!
for name, score in zip(names, scores):
print(f"{name}: {score}점")
# 모든 데이터 사용하려면 itertools.zip_longest() (고급)
|
3. 중첩 for문의 시간 복잡도 주의
1
2
3
4
| # O(n²) - 데이터 많으면 느려짐!
for i in range(1000): # 1000번
for j in range(1000): # × 1000번
pass # 총 100만 번 실행!
|
4. 성능 최적화: 리스트 컴프리헨션 활용
1
2
3
4
5
6
7
| # ❌ 일반 for문
squares = []
for i in range(100):
squares.append(i ** 2)
# ✅ 리스트 컴프리헨션 (더 빠름!)
squares = [i ** 2 for i in range(100)]
|
5. 성능 최적화: 리스트 vs 세트에서 검색
1
2
3
4
5
6
7
8
9
| # ❌ 느림: 리스트에서 검색
numbers_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
if 5 in numbers_list: # 처음부터 하나씩 찾음
pass
# ✅ 빠름: 세트에서 검색
numbers_set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
if 5 in numbers_set: # 바로 찾음!
pass
|
💡 성능 차이: 데이터 1000개일 때 세트가 약 100배 빠름!
6. break를 활용한 조기 종료
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # ❌ 비효율적 (찾았어도 끝까지 확인)
numbers = [1, 3, 5, 7, 9, 11, 13, 15]
target = 7
for num in numbers:
if num == target:
found = True
# 7 찾았는데도 11, 13, 15 계속 확인
# ✅ 효율적 (찾으면 즉시 중단)
for num in numbers:
if num == target:
found = True
break # 찾았으니 끝!
|
🧪 연습 문제
문제 1: zip() 활용
1
2
3
4
5
| # 두 리스트의 같은 위치 원소를 곱해서 새 리스트 만들기
list1 = [1, 2, 3, 4, 5]
list2 = [10, 20, 30, 40, 50]
# 결과: [10, 40, 90, 160, 250]
|
정답 보기
1
2
3
4
5
6
7
8
9
10
11
12
| list1 = [1, 2, 3, 4, 5]
list2 = [10, 20, 30, 40, 50]
result = []
for a, b in zip(list1, list2):
result.append(a * b)
print(result)
# [10, 40, 90, 160, 250]
# 또는 리스트 컴프리헨션
result = [a * b for a, b in zip(list1, list2)]
|
문제 2: 2D 리스트 최댓값
1
2
3
4
5
6
7
8
| # 2D 리스트에서 최댓값과 그 위치 찾기
matrix = [
[3, 7, 2],
[9, 1, 8],
[4, 6, 5]
]
# 출력: 최댓값: 9, 위치: (1, 0)
|
정답 보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| matrix = [
[3, 7, 2],
[9, 1, 8],
[4, 6, 5]
]
max_val = 0
max_row = 0
max_col = 0
for i, row in enumerate(matrix):
for j, val in enumerate(row):
if val > max_val:
max_val = val
max_row = i
max_col = j
print(f"최댓값: {max_val}, 위치: ({max_row}, {max_col})")
|
문제 3: for-else 활용
1
2
3
4
| # 리스트에 짝수가 하나라도 있는지 확인
numbers = [1, 3, 5, 7, 9]
# for-else를 사용해서 구현하세요
|
정답 보기
1
2
3
4
5
6
7
8
| numbers = [1, 3, 5, 7, 9]
for num in numbers:
if num % 2 == 0:
print(f"✅ 짝수 발견: {num}")
break
else:
print("❌ 짝수가 없습니다")
|
📝 오늘 배운 내용 정리
핵심 개념
1. zip()
- 개념: 여러 리스트를 동시 순회
- 활용: 학생 정보 통합, 데이터 비교
2. 중첩 for문
- 개념: for 안에 for
- 활용: 2D 리스트, 조합 생성, 구구단
3. for-else
- 개념: break 없이 끝나면 else 실행
- 활용: 검색 결과 판단, 소수 판별
4. pass
- 개념: 아무것도 안 함 (자리 표시)
- 활용: 코드 스케치, 구조 설계
5. 딕셔너리 순회
.keys(): 키만 .values(): 값만 .items(): 키-값 쌍 ⭐
6. 성능 최적화
- 리스트 컴프리헨션 사용
- 검색에는 세트 활용
- break로 조기 종료
Phase 1 (Day 9) vs Phase 3 (Day 23)
| 항목 | Day 9 (기초) | Day 23 (심화) |
| range() | 기본 사용법 | 성능 고려 |
| enumerate() | 소개 수준 | 실전 활용 |
| 중첩 for | 구구단 예제 | 2D 리스트 탐색 |
| break/continue | 개념 소개 | (Day 22 참조) |
| 새로운 내용 | - | zip(), for-else, pass, 딕셔너리 순회, 최적화 |
❓ 자주 묻는 질문 (FAQ)
Q1. zip()에서 리스트 길이가 다르면 어떻게 되나요?
A: 가장 짧은 리스트에 맞춰서 끝납니다.
1
2
3
4
5
6
| names = ["철수", "영희", "민수"]
scores = [85, 92] # 하나 적음
for name, score in zip(names, scores):
print(f"{name}: {score}점")
# 출력: 철수: 85점, 영희: 92점 (민수는 출력 안 됨)
|
모든 데이터를 사용하려면 itertools.zip_longest()를 사용하세요 (고급 기능).
Q2. for-else는 언제 사용하나요?
A: “찾았다/못 찾았다”를 판단할 때 가장 유용합니다.
1
2
3
4
5
6
7
8
9
10
| # 리스트에서 특정 값 찾기
numbers = [1, 3, 5, 7, 9]
target = 4
for num in numbers:
if num == target:
print(f"✅ {target}를 찾았습니다!")
break
else:
print(f"❌ {target}를 찾지 못했습니다.")
|
Q3. pass, continue, break의 차이는 뭔가요?
A:
- pass: 아무것도 안 함 (코드 자리 표시용)
- continue: 현재 반복만 건너뛰고 다음 반복 진행
- break: 루프 완전 중단
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| for i in range(5):
if i == 2:
pass # 아무것도 안 함
print(i)
# 출력: 0 1 2 3 4
for i in range(5):
if i == 2:
continue # 2만 건너뛰기
print(i)
# 출력: 0 1 3 4
for i in range(5):
if i == 2:
break # 2부터 중단
print(i)
# 출력: 0 1
|
Q4. 딕셔너리 순회할 때 .items() vs .keys()는 어느 걸 써야 하나요?
A: 키와 값을 둘 다 사용한다면 .items(), 키만 필요하다면 .keys() (또는 생략).
1
2
3
4
5
6
7
8
9
| scores = {"철수": 85, "영희": 92}
# 키와 값 둘 다 필요 → .items()
for name, score in scores.items():
print(f"{name}: {score}점")
# 키만 필요 → .keys() (생략 가능)
for name in scores: # 또는 scores.keys()
print(name)
|
Q5. lambda는 뭔가요? 꼭 알아야 하나요?
A: lambda는 간단한 익명 함수를 만드는 방법입니다. 지금은 몰라도 괜찮습니다. Day 26에서 함수를 배운 후 이해하면 됩니다.
1
2
3
4
5
| # 지금은 이렇게만 이해하세요
# lambda x: x[1] → "두 번째 값(x[1])으로 정렬"
sorted_items = sorted(scores.items(), key=lambda x: x[1])
# Day 26 이후에 자세히 배웁니다!
|
Q6. O(n), O(n²) 같은 건 뭔가요?
A: 시간 복잡도라고 하며, 데이터가 늘어날 때 실행 시간이 얼마나 증가하는지를 나타냅니다.
- O(1): 데이터 크기와 상관없이 일정 (예: 세트에서 검색)
- O(n): 데이터 n개면 n번 실행 (예: 리스트 순회)
- O(n²): 데이터 n개면 n×n번 실행 (예: 중첩 for문)
1
2
3
4
5
6
7
8
| # O(n) - 데이터 10개면 10번 실행
for item in items:
print(item)
# O(n²) - 데이터 10개면 100번 실행!
for i in items:
for j in items:
print(i, j)
|
지금은 “중첩 for문은 느릴 수 있다” 정도만 알아두세요!
🔗 관련 자료
📚 이전 학습
Day 22: while문 ⭐⭐⭐
어제는 조건 기반 반복문인 while문과 break/continue를 배웠습니다!
📚 다음 학습
Day 24: 함수 정의하기 (def) ⭐⭐⭐
내일은 코드를 재사용 가능한 블록으로 만드는 함수를 배웁니다!
“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀
| Day 23/100 | Phase 3: 제어문과 함수 | #100DaysOfPython |