본문으로 건너뛰기

딕셔너리와 셋

딕셔너리 (Dictionary)

딕셔너리는 키-값 쌍으로 데이터를 저장하는 변경 가능한 자료구조입니다.

딕셔너리 생성

# 빈 딕셔너리
empty = {}
empty = dict()

# 값이 있는 딕셔너리
person = {
"name": "홍길동",
"age": 25,
"city": "서울"
}

# dict() 함수
person = dict(name="홍길동", age=25, city="서울")

# 리스트에서 변환
pairs = [("name", "홍길동"), ("age", 25)]
person = dict(pairs)

# 키 생성
keys = ["a", "b", "c"]
values = [1, 2, 3]
result = dict(zip(keys, values))
print(result) # {'a': 1, 'b': 2, 'c': 3}

# fromkeys로 기본값 설정
keys = ["a", "b", "c"]
result = dict.fromkeys(keys, 0)
print(result) # {'a': 0, 'b': 0, 'c': 0}

딕셔너리 접근

person = {"name": "홍길동", "age": 25, "city": "서울"}

# 키로 접근
print(person["name"]) # 홍길동
print(person["age"]) # 25

# get() 메서드 (안전)
print(person.get("name")) # 홍길동
print(person.get("phone")) # None (키 없음)
print(person.get("phone", "없음")) # 없음 (기본값)

# 키 존재 확인
print("name" in person) # True
print("phone" in person) # False

# 모든 키, 값, 쌍
print(person.keys()) # dict_keys(['name', 'age', 'city'])
print(person.values()) # dict_values(['홍길동', 25, '서울'])
print(person.items()) # dict_items([('name', '홍길동'), ...])

# 리스트로 변환
keys_list = list(person.keys())
values_list = list(person.values())

딕셔너리 수정

person = {"name": "홍길동", "age": 25}

# 값 추가/수정
person["city"] = "서울" # 추가
person["age"] = 26 # 수정
print(person)

# update() - 여러 값 추가/수정
person.update({"age": 27, "job": "개발자"})
print(person)

person.update(age=28, phone="010-1234-5678")
print(person)

# setdefault - 키가 없을 때만 추가
person.setdefault("city", "부산") # 이미 있으면 무시
print(person["city"]) # 서울 (기존 값 유지)

person.setdefault("hobby", "독서") # 없으면 추가
print(person["hobby"]) # 독서

# 값 삭제
del person["hobby"] # 키로 삭제
age = person.pop("age") # 삭제하고 값 반환
print(age) # 28

# 마지막 항목 삭제 (Python 3.7+는 삽입 순서 유지)
item = person.popitem()
print(item) # ('phone', '010-1234-5678')

# 전체 삭제
person.clear()
print(person) # {}

딕셔너리 반복

person = {"name": "홍길동", "age": 25, "city": "서울"}

# 키 반복
for key in person:
print(key)

# 키 명시적
for key in person.keys():
print(key)

# 값 반복
for value in person.values():
print(value)

# 키-값 쌍 반복 (가장 많이 사용)
for key, value in person.items():
print(f"{key}: {value}")

# 인덱스와 함께
for index, (key, value) in enumerate(person.items()):
print(f"{index+1}. {key}: {value}")

딕셔너리 컴프리헨션

# 기본 형태
squares = {x: x**2 for x in range(1, 6)}
print(squares) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 조건 포함
even_squares = {x: x**2 for x in range(1, 11) if x % 2 == 0}
print(even_squares) # {2: 4, 4: 16, 6: 36, 8: 64, 10: 100}

# 문자열 길이
words = ["apple", "banana", "cherry"]
lengths = {word: len(word) for word in words}
print(lengths) # {'apple': 5, 'banana': 6, 'cherry': 6}

# 키-값 바꾸기
original = {"a": 1, "b": 2, "c": 3}
swapped = {v: k for k, v in original.items()}
print(swapped) # {1: 'a', 2: 'b', 3: 'c'}

# 조건부 변환
scores = {"홍길동": 85, "김철수": 92, "이영희": 78}
grades = {name: "Pass" if score >= 80 else "Fail"
for name, score in scores.items()}
print(grades)

중첩 딕셔너리

# 학생 정보
students = {
"2024001": {
"name": "홍길동",
"age": 20,
"scores": {"math": 85, "english": 90}
},
"2024002": {
"name": "김철수",
"age": 21,
"scores": {"math": 92, "english": 88}
}
}

# 접근
print(students["2024001"]["name"]) # 홍길동
print(students["2024001"]["scores"]["math"]) # 85

# 반복
for student_id, info in students.items():
print(f"ID: {student_id}")
print(f"이름: {info['name']}")
avg = sum(info['scores'].values()) / len(info['scores'])
print(f"평균: {avg:.1f}")
print()

셋 (Set)

은 중복을 허용하지 않는 순서 없는 컬렉션입니다.

셋 생성

# 빈 셋 (주의: {}는 딕셔너리!)
empty = set()

# 값이 있는 셋
numbers = {1, 2, 3, 4, 5}
fruits = {"사과", "바나나", "체리"}

# 중복은 자동 제거
numbers = {1, 2, 2, 3, 3, 3}
print(numbers) # {1, 2, 3}

# 다른 자료형에서 변환
numbers = set([1, 2, 2, 3, 4, 4, 5])
print(numbers) # {1, 2, 3, 4, 5}

chars = set("hello")
print(chars) # {'h', 'e', 'l', 'o'} - 중복 제거됨

셋 연산

# 요소 추가
fruits = {"사과", "바나나"}
fruits.add("체리")
print(fruits) # {'사과', '바나나', '체리'}

# 여러 요소 추가
fruits.update(["포도", "멜론"])
print(fruits)

# 요소 삭제
fruits.remove("바나나") # 없으면 에러
fruits.discard("수박") # 없어도 에러 안 남
popped = fruits.pop() # 임의의 요소 제거
fruits.clear() # 전체 삭제

# 포함 확인
numbers = {1, 2, 3, 4, 5}
print(3 in numbers) # True
print(10 in numbers) # False

# 길이
print(len(numbers)) # 5

집합 연산

a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}

# 합집합 (union)
print(a | b) # {1, 2, 3, 4, 5, 6, 7, 8}
print(a.union(b)) # 같은 결과

# 교집합 (intersection)
print(a & b) # {4, 5}
print(a.intersection(b)) # 같은 결과

# 차집합 (difference)
print(a - b) # {1, 2, 3}
print(a.difference(b)) # 같은 결과

# 대칭 차집합 (symmetric difference)
print(a ^ b) # {1, 2, 3, 6, 7, 8}
print(a.symmetric_difference(b)) # 같은 결과

# 부분집합/상위집합
small = {1, 2}
large = {1, 2, 3, 4}
print(small.issubset(large)) # True
print(large.issuperset(small)) # True
print(small.isdisjoint({5, 6})) # True (교집합 없음)

셋 컴프리헨션

# 기본 형태
squares = {x**2 for x in range(1, 6)}
print(squares) # {1, 4, 9, 16, 25}

# 조건 포함
even_nums = {x for x in range(1, 11) if x % 2 == 0}
print(even_nums) # {2, 4, 6, 8, 10}

# 문자열 처리
text = "hello world"
unique_chars = {c.upper() for c in text if c.isalpha()}
print(unique_chars) # {'H', 'E', 'L', 'O', 'W', 'R', 'D'}

frozenset (불변 셋)

# 불변 셋
immutable = frozenset([1, 2, 3, 4, 5])
print(immutable)

# 수정 불가능
# immutable.add(6) # ❌ AttributeError

# 딕셔너리 키로 사용 가능
data = {
frozenset([1, 2]): "group1",
frozenset([3, 4]): "group2"
}
print(data[frozenset([1, 2])]) # group1

실전 예제

단어 빈도 분석

def word_frequency(text):
"""단어 빈도 계산"""
# 소문자 변환 및 특수문자 제거
words = text.lower().split()

# 빈도 계산
frequency = {}
for word in words:
frequency[word] = frequency.get(word, 0) + 1

# 빈도순 정렬
sorted_freq = sorted(frequency.items(),
key=lambda x: x[1],
reverse=True)

return dict(sorted_freq)

text = """
Python is a popular programming language.
Python is easy to learn.
Many developers use Python.
"""

result = word_frequency(text)
print("=== 단어 빈도 ===")
for word, count in result.items():
print(f"{word}: {count}회")

전화번호부

class PhoneBook:
def __init__(self):
self.contacts = {}

def add(self, name, phone):
"""연락처 추가"""
self.contacts[name] = phone
print(f"{name} 추가됨")

def get(self, name):
"""연락처 조회"""
return self.contacts.get(name, "등록되지 않음")

def update(self, name, phone):
"""연락처 수정"""
if name in self.contacts:
self.contacts[name] = phone
print(f"{name} 수정됨")
else:
print(f"{name}을 찾을 수 없음")

def remove(self, name):
"""연락처 삭제"""
if name in self.contacts:
del self.contacts[name]
print(f"{name} 삭제됨")
else:
print(f"{name}을 찾을 수 없음")

def list_all(self):
"""전체 목록"""
if not self.contacts:
print("등록된 연락처가 없습니다")
return

print("\n=== 전화번호부 ===")
for name, phone in sorted(self.contacts.items()):
print(f"{name}: {phone}")

# 사용
phonebook = PhoneBook()
phonebook.add("홍길동", "010-1234-5678")
phonebook.add("김철수", "010-2345-6789")
phonebook.list_all()
print(f"\n홍길동: {phonebook.get('홍길동')}")

중복 제거 및 분석

def analyze_data(data):
"""데이터 분석"""
# 중복 제거
unique = set(data)

# 통계
stats = {
"전체": len(data),
"고유값": len(unique),
"중복": len(data) - len(unique),
"최대": max(data),
"최소": min(data),
"중복값": [x for x in unique if data.count(x) > 1]
}

return stats

data = [1, 2, 2, 3, 3, 3, 4, 5, 5]
result = analyze_data(data)

print("=== 데이터 분석 ===")
for key, value in result.items():
print(f"{key}: {value}")

학생 점수 관리

class GradeManager:
def __init__(self):
self.students = {}

def add_student(self, name):
"""학생 추가"""
if name not in self.students:
self.students[name] = {}
print(f"{name} 추가됨")

def add_score(self, name, subject, score):
"""점수 추가"""
if name not in self.students:
self.add_student(name)
self.students[name][subject] = score

def get_average(self, name):
"""평균 계산"""
if name not in self.students or not self.students[name]:
return 0
scores = self.students[name].values()
return sum(scores) / len(scores)

def get_subject_average(self, subject):
"""과목별 평균"""
scores = [student[subject]
for student in self.students.values()
if subject in student]
if not scores:
return 0
return sum(scores) / len(scores)

def get_ranking(self):
"""순위 계산"""
averages = {name: self.get_average(name)
for name in self.students}
return sorted(averages.items(),
key=lambda x: x[1],
reverse=True)

def show_report(self):
"""성적표 출력"""
print("\n=== 성적표 ===")
for name, scores in self.students.items():
print(f"\n{name}:")
for subject, score in scores.items():
print(f" {subject}: {score}점")
print(f" 평균: {self.get_average(name):.1f}점")

# 사용
manager = GradeManager()
manager.add_score("홍길동", "수학", 85)
manager.add_score("홍길동", "영어", 90)
manager.add_score("김철수", "수학", 92)
manager.add_score("김철수", "영어", 88)

manager.show_report()

print("\n=== 순위 ===")
for rank, (name, avg) in enumerate(manager.get_ranking(), 1):
print(f"{rank}. {name}: {avg:.1f}점")

태그 시스템

class TagSystem:
def __init__(self):
self.items = {} # {item_id: {tags}}
self.tags = {} # {tag: {item_ids}}

def add_item(self, item_id, tags):
"""항목과 태그 추가"""
self.items[item_id] = set(tags)
for tag in tags:
if tag not in self.tags:
self.tags[tag] = set()
self.tags[tag].add(item_id)

def find_by_tag(self, tag):
"""태그로 항목 찾기"""
return self.tags.get(tag, set())

def find_by_tags(self, tags, mode="any"):
"""여러 태그로 찾기"""
if not tags:
return set()

tag_sets = [self.tags.get(tag, set()) for tag in tags]

if mode == "any": # OR 조건
return set.union(*tag_sets) if tag_sets else set()
else: # AND 조건
return set.intersection(*tag_sets) if tag_sets else set()

def get_related_tags(self, tag):
"""관련 태그 찾기"""
items = self.find_by_tag(tag)
related = set()
for item in items:
related.update(self.items[item])
related.discard(tag)
return related

# 사용
tags = TagSystem()
tags.add_item("post1", ["python", "tutorial", "beginner"])
tags.add_item("post2", ["python", "advanced", "performance"])
tags.add_item("post3", ["javascript", "tutorial", "beginner"])

print("Python 태그:", tags.find_by_tag("python"))
print("Tutorial + Beginner:", tags.find_by_tags(["tutorial", "beginner"], "all"))
print("Python 관련 태그:", tags.get_related_tags("python"))

자주 묻는 질문

Q1. 딕셔너리 키로 사용 가능한 타입은?

A: 불변(immutable) 타입만 가능

# ✅ 가능
d = {
"string": 1,
42: 2,
3.14: 3,
(1, 2): 4,
frozenset([1, 2]): 5
}

# ❌ 불가능
# d = {[1, 2]: 1} # 리스트 불가
# d = {{1, 2}: 1} # 셋 불가
# d = {{"a": 1}: 1} # 딕셔너리 불가

Q2. 딕셔너리는 순서가 있나요?

A: Python 3.7+부터는 삽입 순서 유지

# Python 3.7+
d = {"c": 3, "a": 1, "b": 2}
print(list(d.keys())) # ['c', 'a', 'b'] - 삽입 순서

# 정렬이 필요하면 명시적으로
sorted_d = dict(sorted(d.items()))
print(list(sorted_d.keys())) # ['a', 'b', 'c']

Q3. 셋은 인덱스로 접근할 수 없나요?

A: 네, 순서가 없어서 불가능합니다

s = {1, 2, 3}
# print(s[0]) # ❌ TypeError

# 리스트로 변환하거나 반복문 사용
s_list = list(s)
print(s_list[0]) # ✅

for item in s: # ✅
print(item)

Q4. 딕셔너리 값에 기본값을 설정하려면?

A: defaultdict 사용

from collections import defaultdict

# 일반 딕셔너리
normal = {}
# normal["key"] += 1 # ❌ KeyError

# defaultdict
counter = defaultdict(int) # 기본값 0
counter["key"] += 1 # ✅
print(counter["key"]) # 1

groups = defaultdict(list) # 기본값 []
groups["a"].append(1) # ✅
print(groups) # {'a': [1]}

다음 단계

딕셔너리와 셋을 마스터했습니다!

핵심 정리:
✅ 딕셔너리: 키-값 쌍, 빠른 조회
✅ 셋: 중복 제거, 집합 연산
✅ 컴프리헨션으로 간결한 생성
✅ 실전 활용 (빈도 분석, 태그 시스템 등)

다음 단계: 조건문과 반복문에서 프로그램 흐름 제어를 배워보세요!