[이제와서 시작하는 Python 마스터하기 #5] 리스트, 튜플, 딕셔너리 정복하기
[이제와서 시작하는 Python 마스터하기 #5] 리스트, 튜플, 딕셔너리 정복하기
🍳 5분만에 만드는 레스토랑 주문 시스템
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
def restaurant_order_system():
"""자료구조를 활용한 레스토랑 주문 시스템"""
# 메뉴 (딕셔너리)
menu = {
"한식": {
"비빔밥": 9000,
"된장찌개": 8000,
"김치찌개": 7500,
"불고기": 12000
},
"중식": {
"짜장면": 8000,
"짬뽕밥": 9000,
"탕수육": 12000,
"까슐포": 9500
},
"음료": {
"콜라": 2000,
"사이다": 2000,
"맥주": 4000,
"물": 0
}
}
# 주문 리스트
orders = []
# 테이블별 주문 (튜플로 저장)
table_orders = [
(1, ["비빔밥", "콜라"]),
(2, ["짜장면", "탕수육", "사이다", "맥주"]),
(3, ["불고기", "김치찌개", "콜라", "콜라"])
]
print("🍳 레스토랑 주문 시스템\n")
# 주문 처리
for table_num, items in table_orders:
table_total = 0
order_detail = {"table": table_num, "items": [], "total": 0}
print(f"🏎️ 테이블 {table_num}번 주문:")
for item in items:
# 모든 카테고리에서 아이템 검색
for category, dishes in menu.items():
if item in dishes:
price = dishes[item]
table_total += price
order_detail["items"].append((item, price))
print(f" - {item}: {price:,}원")
break
order_detail["total"] = table_total
orders.append(order_detail)
print(f" 합계: {table_total:,}원\n")
# 전체 통계
total_sales = sum(order["total"] for order in orders)
most_expensive_table = max(orders, key=lambda x: x["total"])
# 인기 메뉴 분석 (리스트 컴프리헨션)
all_items = [item for table, items in table_orders for item in items]
popular_items = {}
for item in all_items:
popular_items[item] = popular_items.get(item, 0) + 1
print("📊 영업 통계:")
print(f"💰 총 매출: {total_sales:,}원")
print(f"🎆 최고 매출 테이블: {most_expensive_table['table']}번 ({most_expensive_table['total']:,}원)")
print(f"🏆 가장 인기 메뉴: {max(popular_items, key=popular_items.get)} ({max(popular_items.values())}개)")
return orders
# 실행
# restaurant_order_system()
📚 Python의 핵심 자료구조
Python에서 데이터를 효율적으로 관리하려면 자료구조를 잘 이해해야 합니다. 이번 포스트에서는 가장 많이 사용되는 세 가지 자료구조를 완벽히 마스터해보겠습니다.
graph TD
A[Python 자료구조] --> B[리스트<br/>List]
A --> C[튜플<br/>Tuple]
A --> D[딕셔너리<br/>Dictionary]
B --> B1[가변<br/>Mutable]
B --> B2[순서 있음<br/>Ordered]
B --> B3[중복 허용<br/>Duplicates OK]
C --> C1[불변<br/>Immutable]
C --> C2[순서 있음<br/>Ordered]
C --> C3[중복 허용<br/>Duplicates OK]
D --> D1[가변<br/>Mutable]
D --> D2[순서 있음<br/>Ordered<br/>(3.7+)]
D --> D3[키 중복 불가<br/>Unique Keys]
[!TIP] 초보자를 위한 비유: 자료구조는 그릇입니다!
- 리스트(List): 아무거나 다 담을 수 있는 장바구니 (순서대로 담기고, 뺄 수도 있음)
- 튜플(Tuple): 한 번 포장하면 못 뜯는 선물 세트 (내용물 변경 불가)
- 딕셔너리(Dictionary): 이름표가 붙은 약 서랍 (이름표(Key)로 내용물(Value)을 찾음)
📋 리스트 (List)
📊 실전 예제: 주식 포트폴리오 관리
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
def stock_portfolio_manager():
"""리스트를 활용한 주식 포트폴리오 관리"""
# 포트폴리오 리스트
portfolio = [
["삼성전자", 100, 70000], # [종목명, 수량, 매수가]
["SK하이닉스", 50, 120000],
["NAVER", 30, 350000],
["카카오", 80, 45000]
]
# 현재가 (딕셔너리로 관리하면 더 효율적이지만 예제를 위해)
current_prices = [68000, 125000, 380000, 48000]
print("📊 주식 포트폴리오 현황\n")
print(f"{'\uc885\ubaa9':^12} {'\uc218\ub7c9':>6} {'\ub9e4\uc218\uac00':>10} {'\ud604\uc7ac\uac00':>10} {'\ud3c9\uac00\uc190\uc775':>12} {'\uc218\uc775\ub960':>8}")
print("-" * 70)
total_invested = 0
total_current = 0
profits = [] # 수익금 리스트
for i, stock in enumerate(portfolio):
name, qty, buy_price = stock
current_price = current_prices[i]
invested = qty * buy_price
current_value = qty * current_price
profit = current_value - invested
profit_rate = (profit / invested) * 100
total_invested += invested
total_current += current_value
profits.append(profit)
# 수익/손실에 따라 이모지
emoji = "📈" if profit > 0 else "📉" if profit < 0 else "➡️"
print(f"{name:12} {qty:6d} {buy_price:10,} {current_price:10,} {profit:+12,.0f} {profit_rate:+7.1f}% {emoji}")
# 통계
total_profit = total_current - total_invested
total_profit_rate = (total_profit / total_invested) * 100
print("-" * 70)
print(f"{'\ud569\uacc4':12} {' ':6} {total_invested:10,} {total_current:10,} {total_profit:+12,.0f} {total_profit_rate:+7.1f}%")
# 분석
print(f"\n📈 최고 수익 종목: {portfolio[profits.index(max(profits))][0]} (+{max(profits):,.0f}원)")
print(f"📉 최대 손실 종목: {portfolio[profits.index(min(profits))][0]} ({min(profits):,.0f}원)")
# 정렬된 포트폴리오 (수익률 기준)
sorted_portfolio = sorted(zip(portfolio, profits),
key=lambda x: x[1], reverse=True)
print("\n🏆 수익률 TOP 3:")
for i, (stock, profit) in enumerate(sorted_portfolio[:3], 1):
print(f" {i}. {stock[0]}: {profit:+,.0f}원")
# 실행
# stock_portfolio_manager()
리스트는 Python에서 가장 많이 사용되는 자료구조로, 여러 값을 순서대로 저장할 수 있습니다.
리스트 생성과 기본 연산
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 리스트 생성
empty_list = []
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True, None] # 다양한 타입 가능
# list() 함수로 생성
chars = list("Python") # ['P', 'y', 't', 'h', 'o', 'n']
range_list = list(range(5)) # [0, 1, 2, 3, 4]
# 인덱싱과 슬라이싱
fruits = ["apple", "banana", "orange", "grape", "kiwi"]
print(fruits[0]) # apple (첫 번째 요소)
print(fruits[-1]) # kiwi (마지막 요소)
print(fruits[1:3]) # ['banana', 'orange'] (슬라이싱)
print(fruits[:3]) # ['apple', 'banana', 'orange']
print(fruits[2:]) # ['orange', 'grape', 'kiwi']
print(fruits[::2]) # ['apple', 'orange', 'kiwi'] (스텝)
print(fruits[::-1]) # ['kiwi', 'grape', 'orange', 'banana', 'apple'] (역순)
# 리스트 수정
fruits[0] = "watermelon" # 요소 변경
fruits[1:3] = ["mango", "peach"] # 슬라이스 변경
리스트 메서드
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
# 추가 메서드
numbers = [1, 2, 3]
numbers.append(4) # 끝에 추가: [1, 2, 3, 4]
numbers.insert(1, 1.5) # 특정 위치 삽입: [1, 1.5, 2, 3, 4]
numbers.extend([5, 6]) # 여러 요소 추가: [1, 1.5, 2, 3, 4, 5, 6]
# append vs extend 차이
list1 = [1, 2, 3]
list1.append([4, 5]) # [1, 2, 3, [4, 5]] - 리스트 자체가 추가
list2 = [1, 2, 3]
list2.extend([4, 5]) # [1, 2, 3, 4, 5] - 요소들이 추가
# 삭제 메서드
items = ["a", "b", "c", "d", "e"]
removed = items.pop() # 마지막 요소 제거 및 반환: 'e'
removed = items.pop(1) # 인덱스 1의 요소 제거: 'b'
items.remove("c") # 첫 번째 'c' 제거
items.clear() # 모든 요소 제거
# del 키워드
numbers = [1, 2, 3, 4, 5]
del numbers[2] # 인덱스 2 삭제: [1, 2, 4, 5]
del numbers[1:3] # 슬라이스 삭제: [1, 5]
# 검색과 정렬
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print(numbers.index(4)) # 2 (4의 인덱스)
print(numbers.count(1)) # 2 (1의 개수)
numbers.sort() # 오름차순 정렬 (원본 수정)
print(numbers) # [1, 1, 2, 3, 4, 5, 6, 9]
numbers.sort(reverse=True) # 내림차순 정렬
numbers.reverse() # 순서 뒤집기
# sorted() 함수 (원본 유지)
original = [3, 1, 4, 1, 5]
sorted_list = sorted(original) # 원본 유지
print(original) # [3, 1, 4, 1, 5]
print(sorted_list) # [1, 1, 3, 4, 5]
리스트 컴프리헨션 심화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 기본 패턴
squares = [x**2 for x in range(10)]
# 조건 포함
even_squares = [x**2 for x in range(10) if x % 2 == 0]
# 다중 조건
numbers = [x for x in range(20) if x % 2 == 0 if x % 3 == 0] # 6의 배수
# 중첩 반복
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [num for row in matrix for num in row]
print(flattened) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 조건부 표현식
result = [x if x > 0 else 0 for x in [-1, 2, -3, 4]]
print(result) # [0, 2, 0, 4]
# 2차원 리스트 생성
grid = [[0 for _ in range(5)] for _ in range(3)]
# [[0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0]]
🔒 튜플 (Tuple)
튜플은 불변(immutable) 시퀀스로, 한 번 생성되면 수정할 수 없습니다.
튜플 생성과 특징
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
# 튜플 생성
empty_tuple = ()
single = (1,) # 콤마 필수! (1)은 정수 1
numbers = (1, 2, 3, 4, 5)
mixed = (1, "hello", 3.14)
# 괄호 없이도 가능 (패킹)
coordinates = 10, 20, 30
print(type(coordinates)) # <class 'tuple'>
# 언패킹
x, y, z = coordinates
print(x, y, z) # 10 20 30
# 확장 언패킹 (Python 3)
first, *middle, last = (1, 2, 3, 4, 5)
print(first) # 1
print(middle) # [2, 3, 4]
print(last) # 5
# 튜플은 불변
# numbers[0] = 10 # TypeError!
# 하지만 내부의 가변 객체는 수정 가능
tuple_with_list = ([1, 2], [3, 4])
tuple_with_list[0].append(3) # OK
print(tuple_with_list) # ([1, 2, 3], [3, 4])
[!TIP] 언제 튜플을 써야 하나요?
“이 데이터는 절대 변하면 안 돼!”라고 확신할 때 튜플을 쓰세요. 예: 좌표(위도, 경도), RGB 색상값(255, 255, 255), 일주일의 요일 등. 실수로 데이터를 바꾸는 것을 막아주기 때문에 안전한 코드를 짤 수 있습니다.
튜플 활용
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
# 함수에서 여러 값 반환
def get_min_max(numbers):
return min(numbers), max(numbers)
min_val, max_val = get_min_max([3, 1, 4, 1, 5, 9])
print(f"최소: {min_val}, 최대: {max_val}") # 최소: 1, 최대: 9
# 값 교환 (스왑)
a, b = 10, 20
a, b = b, a # 튜플 언패킹으로 간단히 교환
print(a, b) # 20 10
# 딕셔너리의 키로 사용 (불변이므로 가능)
locations = {
(37.5, 127.0): "서울",
(35.2, 129.1): "부산",
(35.8, 128.6): "대구"
}
# 네임드 튜플 (collections.namedtuple)
from collections import namedtuple
# Point 클래스 생성
Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
print(p.x, p.y) # 10 20
print(p[0], p[1]) # 10 20 (인덱스로도 접근 가능)
# Person 네임드 튜플
Person = namedtuple('Person', 'name age city')
person = Person('김철수', 30, '서울')
print(f"{person.name}는 {person.city}에 사는 {person.age}살입니다.")
📕 딕셔너리 (Dictionary)
딕셔너리는 키-값 쌍으로 데이터를 저장하는 자료구조입니다.
딕셔너리 생성과 접근
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 딕셔너리 생성
empty_dict = {}
person = {"name": "김파이썬", "age": 25, "city": "서울"}
scores = dict(math=90, english=85, science=88)
# 키를 통한 접근
print(person["name"]) # 김파이썬
print(person.get("age")) # 25
# get() 메서드의 장점
print(person.get("phone")) # None (KeyError 없음)
print(person.get("phone", "없음")) # 없음 (기본값 지정)
# 요소 추가/수정
person["phone"] = "010-1234-5678" # 추가
person["age"] = 26 # 수정
# 여러 요소 업데이트
person.update({"email": "kim@python.com", "age": 27})
# 삭제
del person["phone"]
removed = person.pop("email", None) # 값 반환하며 삭제
person.clear() # 모든 요소 삭제
[!WARNING] 딕셔너리 키(Key)의 조건
딕셔너리의 키(Key)는 반드시 불변(Immutable)이어야 합니다.
- 가능: 정수, 실수, 문자열, 튜플
- 불가능: 리스트, 딕셔너리 (값이 변할 수 있어서 키로 쓸 수 없음)
my_dict = {[1, 2]: "value"}➡️ 에러 발생! (TypeError)
딕셔너리 메서드와 순회
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
# 딕셔너리 메서드
student = {
"name": "이영희",
"scores": {"math": 95, "english": 88},
"grade": "A"
}
# 키, 값, 아이템 가져오기
print(student.keys()) # dict_keys(['name', 'scores', 'grade'])
print(student.values()) # dict_values(['이영희', {...}, 'A'])
print(student.items()) # dict_items([('name', '이영희'), ...])
# 순회
for key in student:
print(f"{key}: {student[key]}")
for key, value in student.items():
print(f"{key}: {value}")
# 중첩 딕셔너리 접근
print(student["scores"]["math"]) # 95
# setdefault() - 키가 없으면 추가
hobbies = student.setdefault("hobbies", [])
hobbies.append("독서")
print(student["hobbies"]) # ['독서']
딕셔너리 컴프리헨션
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 기본 패턴
squares = {x: x**2 for x in range(5)}
print(squares) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 조건 포함
even_squares = {x: x**2 for x in range(10) if x % 2 == 0}
# 키-값 변환
items = [("a", 1), ("b", 2), ("c", 3)]
dict_from_list = {k: v for k, v in items}
# 기존 딕셔너리 변환
prices = {"apple": 1000, "banana": 500, "orange": 800}
discounted = {item: price * 0.8 for item, price in prices.items()}
# 딕셔너리 병합 (Python 3.9+)
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
merged = dict1 | dict2 # {'a': 1, 'b': 3, 'c': 4}
# 이전 버전에서의 병합
merged = {**dict1, **dict2}
🔄 자료구조 간 변환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 리스트 ↔ 튜플
list_data = [1, 2, 3, 4, 5]
tuple_data = tuple(list_data)
back_to_list = list(tuple_data)
# 리스트/튜플 → 딕셔너리
pairs = [("a", 1), ("b", 2), ("c", 3)]
dict_from_pairs = dict(pairs)
# zip()을 사용한 변환
keys = ["name", "age", "city"]
values = ["박민수", 28, "인천"]
person = dict(zip(keys, values))
# 딕셔너리 → 리스트/튜플
dict_data = {"x": 10, "y": 20, "z": 30}
keys_list = list(dict_data.keys())
values_tuple = tuple(dict_data.values())
items_list = list(dict_data.items())
# 중첩 구조 평탄화
nested = [[1, 2], [3, 4], [5, 6]]
flattened = [item for sublist in nested for item in sublist]
💡 실전 예제
1. 학생 성적 관리 시스템
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
class StudentGradeManager:
"""학생 성적 관리 시스템"""
def __init__(self):
self.students = {}
def add_student(self, student_id, name):
"""학생 추가"""
if student_id in self.students:
return False
self.students[student_id] = {
"name": name,
"scores": {},
"attendance": []
}
return True
def add_score(self, student_id, subject, score):
"""과목별 점수 추가"""
if student_id not in self.students:
return False
self.students[student_id]["scores"][subject] = score
return True
def add_attendance(self, student_id, date, status):
"""출석 기록 추가"""
if student_id not in self.students:
return False
self.students[student_id]["attendance"].append({
"date": date,
"status": status # "present", "absent", "late"
})
return True
def get_average_score(self, student_id):
"""평균 점수 계산"""
if student_id not in self.students:
return None
scores = self.students[student_id]["scores"].values()
return sum(scores) / len(scores) if scores else 0
def get_attendance_rate(self, student_id):
"""출석률 계산"""
if student_id not in self.students:
return None
attendance = self.students[student_id]["attendance"]
if not attendance:
return 0
present_count = sum(1 for a in attendance if a["status"] == "present")
return (present_count / len(attendance)) * 100
def get_top_students(self, n=3):
"""상위 n명의 학생"""
student_averages = []
for sid, data in self.students.items():
avg = self.get_average_score(sid)
if avg > 0:
student_averages.append((data["name"], avg))
student_averages.sort(key=lambda x: x[1], reverse=True)
return student_averages[:n]
def generate_report(self, student_id):
"""학생 성적표 생성"""
if student_id not in self.students:
return None
student = self.students[student_id]
avg_score = self.get_average_score(student_id)
attendance_rate = self.get_attendance_rate(student_id)
report = f"""
=== 성적표 ===
학번: {student_id}
이름: {student['name']}
과목별 점수:
"""
for subject, score in student['scores'].items():
report += f" - {subject}: {score}점\n"
report += f"""
평균 점수: {avg_score:.1f}점
출석률: {attendance_rate:.1f}%
"""
return report
# 사용 예시
manager = StudentGradeManager()
# 학생 추가
manager.add_student("2024001", "김철수")
manager.add_student("2024002", "이영희")
manager.add_student("2024003", "박민수")
# 점수 입력
students_scores = {
"2024001": {"국어": 85, "수학": 90, "영어": 78},
"2024002": {"국어": 92, "수학": 88, "영어": 95},
"2024003": {"국어": 78, "수학": 85, "영어": 82}
}
for sid, scores in students_scores.items():
for subject, score in scores.items():
manager.add_score(sid, subject, score)
# 출석 기록
manager.add_attendance("2024001", "2024-03-01", "present")
manager.add_attendance("2024001", "2024-03-02", "present")
manager.add_attendance("2024001", "2024-03-03", "late")
# 상위 학생 조회
print("=== 상위 3명 ===")
for name, avg in manager.get_top_students():
print(f"{name}: 평균 {avg:.1f}점")
# 성적표 출력
print(manager.generate_report("2024001"))
2. 재고 관리 시스템
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
class InventoryManager:
"""재고 관리 시스템"""
def __init__(self):
self.inventory = {} # {상품ID: {"name": 이름, "quantity": 수량, "price": 가격}}
self.transactions = [] # 거래 기록
def add_product(self, product_id, name, quantity=0, price=0):
"""상품 추가"""
if product_id in self.inventory:
return False
self.inventory[product_id] = {
"name": name,
"quantity": quantity,
"price": price,
"history": []
}
return True
def update_stock(self, product_id, quantity_change, transaction_type="adjustment"):
"""재고 수량 업데이트"""
if product_id not in self.inventory:
return False
current = self.inventory[product_id]["quantity"]
new_quantity = current + quantity_change
if new_quantity < 0:
return False # 재고 부족
self.inventory[product_id]["quantity"] = new_quantity
# 거래 기록
transaction = {
"product_id": product_id,
"type": transaction_type, # "sale", "purchase", "adjustment"
"quantity_change": quantity_change,
"timestamp": self._get_timestamp()
}
self.transactions.append(transaction)
self.inventory[product_id]["history"].append(transaction)
return True
def _get_timestamp(self):
"""현재 시간 반환"""
from datetime import datetime
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def sell_product(self, product_id, quantity):
"""상품 판매"""
return self.update_stock(product_id, -quantity, "sale")
def purchase_product(self, product_id, quantity):
"""상품 구매"""
return self.update_stock(product_id, quantity, "purchase")
def get_low_stock_products(self, threshold=10):
"""재고 부족 상품 목록"""
low_stock = []
for pid, data in self.inventory.items():
if data["quantity"] < threshold:
low_stock.append({
"id": pid,
"name": data["name"],
"quantity": data["quantity"]
})
return sorted(low_stock, key=lambda x: x["quantity"])
def get_inventory_value(self):
"""전체 재고 가치 계산"""
total_value = 0
for data in self.inventory.values():
total_value += data["quantity"] * data["price"]
return total_value
def get_product_report(self, product_id):
"""상품별 상세 리포트"""
if product_id not in self.inventory:
return None
product = self.inventory[product_id]
sales = sum(t["quantity_change"] for t in product["history"]
if t["type"] == "sale")
purchases = sum(t["quantity_change"] for t in product["history"]
if t["type"] == "purchase")
return {
"name": product["name"],
"current_stock": product["quantity"],
"price": product["price"],
"total_sales": abs(sales),
"total_purchases": purchases,
"stock_value": product["quantity"] * product["price"]
}
# 사용 예시
inventory = InventoryManager()
# 상품 추가
inventory.add_product("P001", "노트북", 50, 1200000)
inventory.add_product("P002", "마우스", 100, 25000)
inventory.add_product("P003", "키보드", 75, 65000)
# 거래 시뮬레이션
inventory.sell_product("P001", 5)
inventory.sell_product("P002", 20)
inventory.purchase_product("P003", 30)
# 재고 부족 상품 확인
print("=== 재고 부족 상품 ===")
for item in inventory.get_low_stock_products(80):
print(f"{item['name']}: {item['quantity']}개")
# 재고 가치 계산
print(f"\n전체 재고 가치: {inventory.get_inventory_value():,}원")
# 상품별 리포트
report = inventory.get_product_report("P001")
if report:
print(f"\n=== {report['name']} 리포트 ===")
print(f"현재 재고: {report['current_stock']}개")
print(f"총 판매: {report['total_sales']}개")
print(f"재고 가치: {report['stock_value']:,}원")
3. 텍스트 분석기
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
92
93
94
95
96
97
class TextAnalyzer:
"""텍스트 분석 도구"""
def __init__(self):
self.stop_words = {"은", "는", "이", "가", "을", "를", "의", "에", "와", "과"}
def analyze_text(self, text):
"""텍스트 종합 분석"""
words = self._tokenize(text)
return {
"total_words": len(words),
"unique_words": len(set(words)),
"word_frequency": self._count_words(words),
"top_words": self._get_top_words(words, 5),
"sentence_count": text.count('.') + text.count('!') + text.count('?'),
"avg_word_length": self._average_word_length(words),
"longest_word": max(words, key=len) if words else "",
"shortest_word": min(words, key=len) if words else ""
}
def _tokenize(self, text):
"""텍스트를 단어로 분리"""
import re
# 한글, 영어, 숫자만 추출
words = re.findall(r'[가-힣]+|[a-zA-Z]+|[0-9]+', text.lower())
# 불용어 제거
return [w for w in words if w not in self.stop_words]
def _count_words(self, words):
"""단어 빈도 계산"""
frequency = {}
for word in words:
frequency[word] = frequency.get(word, 0) + 1
return frequency
def _get_top_words(self, words, n=5):
"""가장 많이 사용된 단어 n개"""
frequency = self._count_words(words)
sorted_words = sorted(frequency.items(), key=lambda x: x[1], reverse=True)
return sorted_words[:n]
def _average_word_length(self, words):
"""평균 단어 길이"""
if not words:
return 0
return sum(len(word) for word in words) / len(words)
def compare_texts(self, text1, text2):
"""두 텍스트 비교"""
words1 = set(self._tokenize(text1))
words2 = set(self._tokenize(text2))
common_words = words1 & words2
unique_to_text1 = words1 - words2
unique_to_text2 = words2 - words1
# 자카드 유사도 계산
jaccard_similarity = len(common_words) / len(words1 | words2) if words1 or words2 else 0
return {
"common_words": list(common_words),
"unique_to_text1": list(unique_to_text1),
"unique_to_text2": list(unique_to_text2),
"similarity": jaccard_similarity * 100
}
# 사용 예시
analyzer = TextAnalyzer()
# 텍스트 분석
sample_text = """
Python은 매우 강력한 프로그래밍 언어입니다.
Python은 쉽고 읽기 쉬운 문법을 가지고 있으며,
다양한 분야에서 활용됩니다. 데이터 분석, 웹 개발,
인공지능 등 Python이 사용되지 않는 곳이 거의 없습니다.
"""
result = analyzer.analyze_text(sample_text)
print("=== 텍스트 분석 결과 ===")
print(f"총 단어 수: {result['total_words']}")
print(f"고유 단어 수: {result['unique_words']}")
print(f"문장 수: {result['sentence_count']}")
print(f"평균 단어 길이: {result['avg_word_length']:.1f}")
print(f"가장 긴 단어: {result['longest_word']}")
print("\n가장 많이 사용된 단어 TOP 5:")
for word, count in result['top_words']:
print(f" - {word}: {count}회")
# 텍스트 비교
text1 = "Python은 데이터 분석에 최적화된 언어입니다."
text2 = "Python은 웹 개발에도 많이 사용되는 언어입니다."
comparison = analyzer.compare_texts(text1, text2)
print(f"\n두 텍스트의 유사도: {comparison['similarity']:.1f}%")
print(f"공통 단어: {comparison['common_words']}")
⚠️ 초보자가 자주 하는 실수
1. 리스트 복사 실수
1
2
3
4
5
6
7
8
9
10
11
12
# ❌ 얕은 복사의 문제
list1 = [[1, 2], [3, 4]]
list2 = list1.copy() # 또는 list1[:]
list2[0][0] = 99
print(list1) # [[99, 2], [3, 4]] - 원본도 변경됨!
# ✅ 깊은 복사
import copy
list1 = [[1, 2], [3, 4]]
list2 = copy.deepcopy(list1)
list2[0][0] = 99
print(list1) # [[1, 2], [3, 4]] - 원본 유지
2. 리스트 초기화 실수
1
2
3
4
5
6
7
8
9
# ❌ 같은 리스트 참조
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]]
3. 딕셔너리 키 실수
1
2
3
4
5
6
7
# ❌ 변경 가능한 객체를 키로 사용
# dict_wrong = {[1, 2]: "value"} # TypeError!
# ✅ 불변 객체를 키로 사용
dict_right = {(1, 2): "value"} # 튜플은 OK
dict_right = {"key": "value"} # 문자열 OK
dict_right = {42: "value"} # 숫자 OK
4. 딕셔너리 순회 중 수정
1
2
3
4
5
6
7
8
9
10
11
# ❌ 순회 중 딕셔너리 크기 변경
data = {"a": 1, "b": 2, "c": 3}
for key in data:
if data[key] < 3:
del data[key] # RuntimeError!
# ✅ 복사본이나 리스트로 변환
data = {"a": 1, "b": 2, "c": 3}
for key in list(data.keys()):
if data[key] < 3:
del data[key]
5. get() 메서드 미사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ❌ KeyError 위험
counts = {}
word = "hello"
# counts[word] += 1 # KeyError!
# ✅ get() 메서드 사용
counts = {}
word = "hello"
counts[word] = counts.get(word, 0) + 1
# 또는 defaultdict 사용
from collections import defaultdict
counts = defaultdict(int)
counts[word] += 1 # 자동으로 0으로 초기화
🎯 핵심 정리
자료구조 선택 가이드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 리스트를 사용해야 할 때
# - 순서가 중요한 데이터
# - 인덱스로 접근이 필요한 경우
# - 데이터가 자주 변경되는 경우
shopping_cart = ["apple", "banana", "orange"]
# 튜플을 사용해야 할 때
# - 변경되지 않아야 하는 데이터
# - 딕셔너리의 키로 사용
# - 함수에서 여러 값을 반환
coordinates = (37.5665, 126.9780) # 서울의 위도, 경도
# 딕셔너리를 사용해야 할 때
# - 키-값 쌍으로 데이터 관리
# - 빠른 검색이 필요한 경우
# - 데이터에 의미 있는 레이블이 필요한 경우
user_info = {"id": "user123", "name": "김파이썬", "age": 25}
성능 비교
| 연산 | 리스트 | 튜플 | 딕셔너리 |
|---|---|---|---|
| 인덱스 접근 | O(1) | O(1) | - |
| 키 접근 | - | - | O(1) |
| 추가(끝) | O(1) | 불가 | O(1) |
| 삽입(중간) | O(n) | 불가 | O(1) |
| 삭제 | O(n) | 불가 | O(1) |
| 검색 | O(n) | O(n) | O(1) |
| 메모리 | 중간 | 적음 | 많음 |
메모리 효율적인 사용
1
2
3
4
5
6
7
8
9
# 제너레이터 표현식 (메모리 효율적)
sum_of_squares = sum(x**2 for x in range(1000000))
# 리스트 컴프리헨션 (전체 리스트 생성)
squares_list = [x**2 for x in range(1000000)] # 메모리 많이 사용
# 필요한 경우만 리스트로 변환
gen = (x**2 for x in range(10))
when_needed = list(gen)
🎓 파이썬 마스터하기 시리즈
📚 기초편 (1-7)
- Python 소개와 개발 환경 설정 완벽 가이드
- 변수, 자료형, 연산자 완벽 정리
- 조건문과 반복문 마스터하기
- 함수와 람다 완벽 가이드
- 리스트, 튜플, 딕셔너리 정복하기
- 문자열 처리와 정규표현식
- 파일 입출력과 예외 처리
🚀 중급편 (8-12)
💼 고급편 (13-16)
이전글: 함수와 람다 완벽 가이드 ⬅️ 현재글: 리스트, 튜플, 딕셔너리 정복하기 다음글: 문자열 처리와 정규표현식 ➡️
이번 포스트에서는 Python의 핵심 자료구조인 리스트, 튜플, 딕셔너리를 완벽히 정복했습니다. 다음 포스트에서는 문자열을 다루는 다양한 방법과 강력한 정규표현식에 대해 알아보겠습니다. Happy Coding! 🐍✨
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.