리스트와 튜플은 순서대로 값을 저장하는 자료구조였습니다. 하지만 실제 프로그래밍에서는 "이름과 전화번호", "학생 이름과 점수"처럼 연관된 값을 묶어서 저장해야 하는 경우가 많습니다. 이럴 때 사용하는 것이 바로 딕셔너리(Dictionary)입니다.
딕셔너리는 키(key)와 값(value)의 쌍으로 데이터를 저장하는 자료형입니다. 마치 사전에서 단어(키)를 찾아 뜻(값)을 확인하듯이, 키를 통해 값을 빠르게 가져올 수 있어서 딕셔너리라고 부릅니다.
# 리스트: 인덱스로 접근
scores_list = [85, 90, 78]
print(scores_list[0]) # 85 (첫 번째 학생의 점수)
# 문제: 이 점수가 누구의 점수인지 알 수 없음
# 딕셔너리: 키로 접근
scores_dict = {'Alice': 85, 'Bob': 90, 'Charlie': 78}
print(scores_dict['Alice']) # 85
# 훨씬 직관적!
딕셔너리는 중괄호({})를 사용하며, 키: 값 형식으로 저장하고 쉼표(,)로 구분합니다.
# 기본 형식
딕셔너리 = {키1: 값1, 키2: 값2, 키3: 값3}
# 학생 점수 딕셔너리
scores = {
'Alice': 85,
'Bob': 90,
'Charlie': 78
}
print(scores) # {'Alice': 85, 'Bob': 90, 'Charlie': 78}
# 사용자 정보 딕셔너리
user = {
'name': '홍길동',
'age': 25,
'city': 'Seoul',
'is_member': True
}
print(user)
키가 중복되면 가장 뒤에 있는 값만 사용됩니다.
scores = {
'Alice': 85,
'Bob': 90,
'Alice': 95 # 중복!
}
print(scores) # {'Alice': 95, 'Bob': 90}
# 'Alice'의 첫 번째 값(85)은 무시됨
키는 불변(immutable) 자료형만 사용할 수 있습니다.
✅ 사용 가능:
# 문자열 키
d1 = {'name': 'Alice', 'age': 25}
# 숫자 키
d2 = {1: 'one', 2: 'two', 3: 'three'}
# 튜플 키 (좌표 등에 유용)
d3 = {(0, 0): 'origin', (10, 20): 'point A'}
# 혼합도 가능
d4 = {'name': 'Alice', 1: 'first', (0, 0): 'origin'}
print(d3[(0, 0)]) # origin
❌ 사용 불가:
# 에러!
# d = {[1, 2]: 'value'} # TypeError: unhashable type: 'list'
# d = {{'key': 1}: 'value'} # TypeError
값에는 모든 자료형을 사용할 수 있습니다.
data = {
'name': 'Alice', # 문자열
'age': 25, # 정수
'height': 165.5, # 실수
'hobbies': ['reading', 'coding'], # 리스트
'address': {'city': 'Seoul', 'zipcode': '12345'}, # 딕셔너리
'is_student': True, # 불
'grades': (90, 85, 88) # 튜플
}
print(data['hobbies']) # ['reading', 'coding']
print(data['address']['city']) # Seoul
# 방법 1: 빈 중괄호
empty1 = {}
# 방법 2: dict() 함수
empty2 = dict()
print(empty1) # {}
print(empty2) # {}
print(type(empty1)) # <class 'dict'>
dict() 함수에서 키=값 형식으로 만들 수 있습니다. 주의: 키에 따옴표를 사용하면 안 됩니다.
# dict() 사용
scores = dict(Alice=85, Bob=90, Charlie=78)
print(scores) # {'Alice': 85, 'Bob': 90, 'Charlie': 78}
# 키는 자동으로 문자열이 됨
print(type(list(scores.keys())[0])) # <class 'str'>
주의: 이 방법은 키가 문자열이고 변수명 규칙을 따라야 합니다.
# 올바른 예
user = dict(name='Alice', age=25)
# 잘못된 예 (키에 공백, 특수문자 불가)
# user = dict(first name='Alice') # SyntaxError
# user = dict(user-id=123) # SyntaxError
두 개의 리스트를 묶어 딕셔너리를 만들 수 있습니다.
keys = ['Alice', 'Bob', 'Charlie']
values = [85, 90, 78]
scores = dict(zip(keys, values))
print(scores) # {'Alice': 85, 'Bob': 90, 'Charlie': 78}
zip() 함수는 두 리스트를 같은 인덱스끼리 묶어줍니다.
# zip의 동작 원리
keys = ['a', 'b', 'c']
values = [1, 2, 3]
zipped = list(zip(keys, values))
print(zipped) # [('a', 1), ('b', 2), ('c', 3)]
# 딕셔너리로 변환
d = dict(zip(keys, values))
print(d) # {'a': 1, 'b': 2, 'c': 3}
리스트 안에 (키, 값) 형태의 튜플을 나열하여 딕셔너리를 만들 수 있습니다.
# 튜플 리스트를 dict()로 변환
scores = dict([('Alice', 85), ('Bob', 90), ('Charlie', 78)])
print(scores) # {'Alice': 85, 'Bob': 90, 'Charlie': 78}
이 방법은 데이터베이스나 CSV에서 읽은 데이터를 딕셔너리로 변환할 때 유용합니다.
dict() 함수 안에 중괄호로 딕셔너리를 만들 수 있습니다.
scores = dict({'Alice': 85, 'Bob': 90, 'Charlie': 78})
print(scores) # {'Alice': 85, 'Bob': 90, 'Charlie': 78}
하지만 이 방법은 그냥 중괄호만 사용하는 것과 동일하므로, 일반적으로는 사용하지 않습니다.
# 이게 더 간단함
scores = {'Alice': 85, 'Bob': 90, 'Charlie': 78}
딕셔너리의 값에 접근할 때는 대괄호([]) 안에 키를 넣습니다.
scores = {'Alice': 85, 'Bob': 90, 'Charlie': 78}
# 키로 값 가져오기
print(scores['Alice']) # 85
print(scores['Bob']) # 90
print(scores['Charlie']) # 78
# 변수로 접근
name = 'Alice'
print(scores[name]) # 85
키에 접근한 뒤 새로운 값을 할당하면 값이 변경됩니다.
scores = {'Alice': 85, 'Bob': 90, 'Charlie': 78}
# 값 변경
scores['Alice'] = 95
print(scores) # {'Alice': 95, 'Bob': 90, 'Charlie': 78}
scores['Bob'] = 100
print(scores) # {'Alice': 95, 'Bob': 100, 'Charlie': 78}
중요: 딕셔너리는 없는 키에 값을 할당하면 해당 키가 자동으로 추가됩니다.
scores = {'Alice': 85, 'Bob': 90}
# 새로운 키 추가
scores['Charlie'] = 78
print(scores) # {'Alice': 85, 'Bob': 90, 'Charlie': 78}
scores['David'] = 92
print(scores) # {'Alice': 85, 'Bob': 90, 'Charlie': 78, 'David': 92}
이것이 리스트와의 큰 차이점입니다. 리스트는 존재하지 않는 인덱스에 접근하면 에러가 발생하지만, 딕셔너리는 새로운 키-값 쌍이 추가됩니다.
없는 키의 값을 가져오려고 하면 KeyError가 발생합니다.
scores = {'Alice': 85, 'Bob': 90}
# 에러 발생!
# print(scores['Charlie']) # KeyError: 'Charlie'
키가 있는지 먼저 확인한 후 접근합니다.
scores = {'Alice': 85, 'Bob': 90}
if 'Charlie' in scores:
print(scores['Charlie'])
else:
print('Charlie는 딕셔너리에 없습니다')
# Charlie는 딕셔너리에 없습니다
get() 메서드는 키가 없어도 에러를 발생시키지 않고 None을 반환합니다.
scores = {'Alice': 85, 'Bob': 90}
print(scores.get('Alice')) # 85
print(scores.get('Charlie')) # None
# 기본값 지정
print(scores.get('Charlie', 0)) # 0 (키가 없으면 0 반환)
in 연산자로 딕셔너리에 키가 있는지 확인할 수 있습니다.
scores = {'Alice': 85, 'Bob': 90, 'Charlie': 78}
print('Alice' in scores) # True
print('David' in scores) # False
# not in도 사용 가능
print('David' not in scores) # True
주의: in 연산자는 키의 존재를 확인하지, 값은 확인하지 않습니다.
scores = {'Alice': 85, 'Bob': 90}
print('Alice' in scores) # True (키 확인)
print(85 in scores) # False (85는 값이지 키가 아님)
# 값 존재 확인
print(85 in scores.values()) # True
len() 함수로 딕셔너리의 키-값 쌍의 개수를 확인할 수 있습니다.
scores = {'Alice': 85, 'Bob': 90, 'Charlie': 78}
print(len(scores)) # 3
# 빈 딕셔너리
empty = {}
print(len(empty)) # 0
# 키 추가 후
scores['David'] = 92
print(len(scores)) # 4
딕셔너리의 키 개수 = 값의 개수입니다 (키와 값은 항상 쌍으로 존재).