13. 사전과 집합#
두 개의 비순차 자료형 사전과 집합을 소개한다. 비순차 자료형은 문자열, 리스트, 튜플과는 달리 순서를 따지지 않으며 항목의 중복도 허용하지 않는다. 사전은 프로그래밍언어에 따라 연관배열, 맵 등으로 불리기도 하며 매우 다양하게 활용된다. 집합과 사전은 해시 가능한 값만을 이용하기에 해시 가능성도 함께 소개한다. 이후에는 논리식을 이용하여 간편하게 리스트와 정의하는 조건제시법을 다룬다.
13.1. 사전#
사전 자료형은 키key와 값value의
쌍으로 구성된 항목들의 집합이며, 딕셔너리dictionary 라고도 불린다.
예를 들어, 'Hello'
와 'World'
를 키로, '안녕'
, '세계'
를 각각의 키에 대한 값으로
갖는 사전은 다음과 같다.
{'Hello':'안녕', 'World':'세계'}
빈 사전
빈 사전은 아무것도 포함하지 않는 사전을 의미한다. 빈 사전를 만드는 방법은 아래와 같다.
empty_dict = {}
empty_dict
{}
다음 방식도 가능하다.
empty_dict = dict()
empty_dict
{}
dict()
함수
키-값의 튜플 형식의 항목을 갖는 모음 자료형을 사전으로 변환한다. 단, 키로 사용되는 값은 해시 가능해야 한다.
data = [('Hello', '안녕'), ('World', '세계'), ('Programming', '프로그래밍')]
dict(data)
{'Hello': '안녕', 'World': '세계', 'Programming': '프로그래밍'}
zip()
함수를 활용하면 편리하다.
data = zip('abcde', [1, 2, 3, 4, 5])
data_dict = dict(data)
data_dict
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
사전과 반복문
사전에 대한 반복문은 키key에 대해 실행된다.
for item in data_dict:
print(item, end=' ')
a b c d e
키와 값의 쌍에 대해 반복문을 실행하려면 items()
메서드를 이용한다.
단, 키와 값 각각에 대해 변수를 지정하는 게 좋다.
for key, value in data_dict.items():
print(f"{key:>8} 키의 값: {value}")
a 키의 값: 1
b 키의 값: 2
c 키의 값: 3
d 키의 값: 4
e 키의 값: 5
항목을 쪼개서 사용할 수도 있다.
for item in data_dict.items():
key = item[0]
value = item[1]
print(f"{key:>8} 키의 값: {value}")
a 키의 값: 1
b 키의 값: 2
c 키의 값: 3
d 키의 값: 4
e 키의 값: 5
값에 대해 반복문을 실행하려면 values()
메서드를 이용한다.
for item in data_dict.values():
print(item, end=' ')
1 2 3 4 5
in
연산자
사전의 키로 사용되었는 여부를 알려주는 논리 연산자다.
'city' in {"name": "강현", "age": "3"}
False
in
의 부정은 not in
을 사용한다. 즉
키로 사용되지 않았는지 여부를 뭍는다.
'city' not in {"name": "강현", "age": "3"}
True
len()
함수
사전에 포함된 항목의 개수를 반환한다.
len({"name": "강현", "age": "3"})
2
13.1.1. 사전 키 인덱싱#
사전에 사용된 키를 이용한 인덱싱이 지원된다.
예를 들어, 아래 딕셔너리 dic
에서 'Hello'
에 대응하는 값을 확인하고자 하면
다음과 같이 대괄호를 사용하는 인덱싱을 이용한다.
dic = {'Hello' : '안녕', 'World' : '세계'}
dic['Hello']
'안녕'
존재하지 않는 키로 값을 추출하려고 하면, 오류가 발생한다.
dic['Python']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
Cell In[13], line 1
----> 1 dic['Python']
KeyError: 'Python'
13.1.2. 항목 추가, 업데이트, 삭제#
아래와 같은 형식으로 사전에 항목을 추가하거나 업데이트 한다.
사전[key] = value
예를 들어, dic
에 Python : 파이썬
항목을 다음과 같이 추가할 수 있다.
dic['Python'] = '파이썬'
dic
{'Hello': '안녕', 'World': '세계', 'Python': '파이썬'}
이미 사용된 키를 이용하면 키와 연결된 값이 업데이트 된다.
dic['World'] = '세상'
dic
{'Hello': '안녕', 'World': '세상', 'Python': '파이썬'}
사전의 항목은 del
명령어에 키를 지정하여 삭제한다.
del dic['World']
dic
{'Hello': '안녕', 'Python': '파이썬'}
13.1.3. 사전 메서드#
사전 자료형이 제공하는 주요 메서드는 아래와 같다.
기능 |
메서드 |
설명 |
---|---|---|
키 확인 |
|
사전에 사용된 키로 구성된 순차 자료형 값 반환 |
값 확인 |
|
사전에 사용된 값으로 구성된 순차 자료형 값 반환 |
키와 값 확인 |
|
사전에 사용된 키와 값의 순서쌍으로 구성된 순차 자료형 값 반환 |
값 반환 |
|
지정된 키에 대한 값 반환 |
dic = {'Hello' : '안녕', 'World' : '세계'}
keys()
메서드: 키로 이루어진 모음 자료형 반환
dic.keys()
dict_keys(['Hello', 'World'])
values()
메서드: 값으로 이루어진 모음 자료형 반환
dic.values()
dict_values(['안녕', '세계'])
items()
메서드: (키, 값) 모양의 쌍으로 이루어진 모음 자료형 반환
dic.items()
dict_items([('Hello', '안녕'), ('World', '세계')])
get()
메서드: 지정된 키와 연관된 값 반환
dic.get('Hello')
'안녕'
존재하지 않는 키를 사용하면 기본값으로 None
이 사용된다.
dic.get('hello')
키가 사용되지 않았을 때 기본값을 바꾸려면 둘째 인자를 지정한다.
dic.get('hello', "해당 키가 없어요.")
'해당 키가 없어요.'
예제
6명 정보를 이용하여 키는 이름, 값은 해당 이름의 정보를 담은 리스트를 사용하는 사전을
가리키는 info_dict
변수를 선언하라.
info_list = [['김강현', '010-1234-5678', 20, 172.5, '제주'],
['황현', '02-9871-1234', 19, 163.5, '서울'],
['남궁수현', '01-3456-7891', 21, 156.7, '경기'],
['최흥선', '070-4321-1111', 21, 187.2, '부산'],
['김선주', '010-3333-8888', 22, 164.6, '광주'],
['함중아', '010-7654-2345', 18, 178.3, '강원']]
답:
6명 각자의 이름을 키로, 나머지 정보로 구성된 리스트를 값으로 사용하는 사전을 구성한다.
이를 위해 for
반복문과 12.4절의 리스트 해체를 이용한다.
먼저 info_list
의 항목은 아래 형식임에 주의한다.
person = ['김강현', '010-1234-5678', 20, 172.5, '제주']
위 리스트를 이름과 나머지로 구분하기 위해 다음과 같이 리스트 해체를 이용한다.
name, *rest = person
그러면 name
은 이름을, rest
는 나머지 항목으로 구성된 리스트를 가리키게 되어
두 변수를 다음과 같이 선언한 것과 동일하다.
name = person[0]
rest = person[1:]
위 설명을 정리해서 info_dict
변수를 아래와 같이 선언한다.
info_dict = dict()
for person in info_list:
name, *rest = person
info_dict[name] = rest
info_dict
{'김강현': ['010-1234-5678', 20, 172.5, '제주'],
'황현': ['02-9871-1234', 19, 163.5, '서울'],
'남궁수현': ['01-3456-7891', 21, 156.7, '경기'],
'최흥선': ['070-4321-1111', 21, 187.2, '부산'],
'김선주': ['010-3333-8888', 22, 164.6, '광주'],
'함중아': ['010-7654-2345', 18, 178.3, '강원']}
예를 들어 김선주의 정보를 보고 싶으면 다음과 같이 실행한다.
info_dict['김선주']
['010-3333-8888', 22, 164.6, '광주']
예제
이름을 물으면 나이를 확인해주는 name_age()
함수를 선언하라.
단, 이름이 없으면 “정보 없음”을 반환해야 한다.
힌트: get()
사전 메서드 활용
답:
앞서 선언한 info_dict
변수를 활용할 수 있다.
이유는 이름을 사용하여 인덱싱을 적용하면 전화번호, 나이, 키, 출생지 정보로 구성된 리스트가 확인된다.
따라서 인덱싱으로 쉽게 나이를 확인할 수 있다.
그런데 키 인덱싱을 활용할 때 키가 사용되지 않았다면 오류가 발생한다.
따라서 try-except
명령문을 이용하여 다음과 같이 name_age()
함수를 구현할 수 있다.
def name_age(name):
try:
info = info_dict[name]
return info[1]
except KeyError:
return "정보없음"
반면에 get()
메서드를 활용하면 오류 없이 코드가 실행된다.
다면 반환값이 None
인지 여부를 구분해야 한다.
def name_age(name):
info = info_dict.get(name)
# info가 None이 아닌지 여부 판단
if info:
return info[1]
else:
return "정보없음"
그런데 사전에 키가 없을 때 반환되는 값을 정보 없음
으로 지정할 수 있다.
다만 이름이 정보에 포함된 경우 1번 인덱스의 값을 반환해야 하기에
정보가 없을 때에도 정보 없음
이 어떤 리스트의 1번 인덱스의 값으로 위치시키는 게 좋다.
그러면 리스트 인덱싱을 결과에 상관없이 적용할 수 있다.
결과적으로 다음과 같이 name_age()
함수를 정의할 수 있다.
def name_age(name):
age = info_dict.get(name, [None, '정보 없음'])[1]
return age
김선주의 나이를 확인하자.
name_age('김선주')
22
함중아의 나이는?
name_age('함중아')
18
반면에 최강현은 정보가 없다.
name_age('최강현')
'정보 없음'
13.2. 집합#
집합 자료형은 수학에서 다루는 집합처럼 작동하도록 만든 비순차 모음 자료형이며 다음과 같이 중괄호를 사용하여 정의된다. 집합은 항목의 중복을 허용하지 않고, 항목들 사이의 순서 또한 무시된다. 따라서 인덱싱이나 슬라이싱을 지원하지 않는다.
{항목1, 항목2, 항목3, ..., 항목n}
a_set = {4, 4, 9.2, "apple", True, 4}
a_set
{4, 9.2, True, 'apple'}
공집합
공집합empty set은 아무것도 포함하지 않는 집합을 의미한다. 공집합를 만드는 방법은 아래와 같다.
empty_set = set()
empty_set
set()
{}
은 빈 사전을 가리킨에 주의한다.
a = {}
type(a)
dict
set()
함수
임의의 모음 자료형을 집합으로 변환한다. 이 과정에서 순서와 중복이 무시된다.
set([5, 1, 1, 2, 5, 5, 1])
{1, 2, 5}
set((1, 3, 3, 9, 1))
{1, 3, 9}
순차 자료형의 항목에서 중복을 제거하고 싶을 때 set()
함수가 유용하게 활용된다.
예를 들어 아래 코드는 리스트에서 중복된 항목이 삭제된,
하지만 동일한 항목을 포함한 새로운 리스트를 생성한다.
단, 기존 리스트에서 사용된 항목들의 순서는 보존되지 않음에 주의한다.
list(set([5, 1, 1, 2, 5, 5, 1]))
[1, 2, 5]
in
연산자
집합의 항목(원소)으로 등장하는지 여부를 알려주는 논리 연산자다.
1 in {1, 2, 3, 9, 4}
True
'a' not in {1, 'b', True, 9}
True
len()
함수
집합에 포함된 항목의 개수를 반환한다.
len({1, 3, 5, 7, 9})
5
13.2.1. 집합 메서드#
집합 자료형이 제공하는 주요 메서드는 아래와 같다.
기능 |
메서드 |
설명 |
---|---|---|
합집합 |
|
|
교집합 |
|
|
차집합 |
|
|
부분집합 여부 |
|
|
항목 추가 |
|
|
학목 삭제 |
|
|
여기서는 집합 메서드를 자세히 다루지 않는다. 이유는 각각의 메서드의 기능이 중, 고등학교에서 배운 집합 연산과 동일하기 때문이다.
합집합 생성
A = {1, 2, 3}
B = {2, 4}
A.union(B)
{1, 2, 3, 4}
교집합 생성
A.intersection(B)
{2}
차집합 생성
A.difference(B)
{1, 3}
부분집합 여부 확인
A.issubset(B)
False
A.issubset(A.union(B))
True
항목(원소) 추가
A.add(5)
A
{1, 2, 3, 5}
항목(원소) 삭제
A.remove(5)
A
{1, 2, 3}
여기서는 아래 예제를 이용하여 중복을 없애는 set()
함수의 유용성을 소개한다.
예제
info_dict
를 이용하여 키는 나이, 값은 해당 나이를 갖는 사람의 수를 사용하는 사전을 가리키는
age_count_dict
를 선언하라.
답:
info_dict
는 다음과 같다.
info_dict
{'김강현': ['010-1234-5678', 20, 172.5, '제주'],
'황현': ['02-9871-1234', 19, 163.5, '서울'],
'남궁수현': ['01-3456-7891', 21, 156.7, '경기'],
'최흥선': ['070-4321-1111', 21, 187.2, '부산'],
'김선주': ['010-3333-8888', 22, 164.6, '광주'],
'함중아': ['010-7654-2345', 18, 178.3, '강원']}
먼저 나이로 구성된 리스트를 생성한다.
ages = []
for person in info_list:
ages.append(person[2])
print(ages)
[20, 19, 21, 21, 22, 18]
리스트에 포함된 나이를 확인하기 위해 중복을 제거한다. 이를 위해 집합 자료형으로 변환시킨다.
ages_set = set(ages)
ages_set
{18, 19, 20, 21, 22}
ages_set
집합에 포함된 각각의 항목을 대상으로 count()
리스트 메서드를 적용하면 원하는
사전을 생성할 수 있다.
age_count_dict = dict()
for age in ages_set:
age_count_dict[age] = ages.count(age)
age_count_dict
{18: 1, 19: 1, 20: 1, 21: 2, 22: 1}
13.2.2. 해시 가능성#
집합의 항목으로 리스트, 사전, 집합과 같은 가변 자료형은 사용할 수 없다.
{[1, 3], 4}
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[58], line 1
----> 1 {[1, 3], 4}
TypeError: unhashable type: 'list'
사전의 키 또한 리스트를 키로 사용할 수 없다.
no_list_keys = {[1, 2]: "리스트는 키로 사용할 수 없음"}
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[59], line 1
----> 1 no_list_keys = {[1, 2]: "리스트는 키로 사용할 수 없음"}
TypeError: unhashable type: 'list'
이유는 리스트 등의 가변 자료형은 해시 가능하지 않기 때문이다.
hash()
함수
파이썬 객체의 해시 가능성은 hash()
함수의 인자로 사용될 수 있는가에 의해 결정된다.
즉 hash()
함수와 함께 호출됐을 때 오류가 발생하지 않아야 한다.
이렇게 hash()
함수와 함께 호출되었을 때 오류가 발생하지 않은 값을
해시 가능hashable하다고 부른다.
해시 가능한 값에 대해 hash()
함수의 반환값은 특정 정수이며
서로 다른 두 값은 서로 다른 반환값을 갖는다.
즉, 조금이라도 다른 해시 가능한 값이 인자로 지정되면 다른 값을 계산한다.
hash('123')
-6307178578295685641
hash('123 ')
3543337771683958828
hash((1, 3, 4))
-1070212610609621906
hash((1, 2, 4))
-4363729961677198915
해시 함수의 반환값
hash()
함수의 반환값이 실행할 때마다 달라질 수 있다.
이는 보안상의 이유로 무작위 기능이 적용되기 때문이다.
항상 고정된 값을 반환하는 해시 함수도 있지만 여기서는 다루지 않는다.
반면에 리스트 등 가변 자료형의 객체는 해시 가능하지 않다.
hash([1, 3])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[64], line 1
----> 1 hash([1, 3])
TypeError: unhashable type: 'list'
리스트를 포함한 튜플도 해시 가능하지 않다.
hash(([1, 2], 3))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[65], line 1
----> 1 hash(([1, 2], 3))
TypeError: unhashable type: 'list'
집합에 해시 가능한 값만 포함될 수 있는 이유는 두 항목을 구별하기 위해 hash()
함수를 이용하기 때문이다.
반면에 리스트 등과 같은 가변 자료형의 값은 언제든 변할 수 있기에 정체를
제대로 파악할 수 없다.
13.3. 조건제시법#
조건제시법comprehension을 이용하여 리스트, 집합, 사전을 정의할 수 있다.
리스트 조건제시법
수학에서 0과 10사이에 있는 홀수들의 제곱을 원소로 갖는 집합을 조건제시법으로 표현하면 다음과 같다.
0과 10 사이에 있는 홀수들의 제곱을 항목으로 갖는 리스트를 for
반복문으로 구현해 보자.
zero2ten_odd = []
for x in range(11):
if x%2 == 1:
zero2ten_odd.append(x**2)
zero2ten_odd
[1, 9, 25, 49, 81]
조건제시법을 이용하여 보다 간단하게 리스트를 생성할 수 있다.
zero2ten_odd = [x**2 for x in range(11) if x%2 == 1]
zero2ten_odd
[1, 9, 25, 49, 81]
위 두 코드를 비교하면 조건제시법의 작동원리를 이해할 수 있을 것이다.
집합 조건제시법
위 결과를 집합으로 구현하면 다음과 같다.
zero2ten_odd_set = {x**2 for x in range(11) if x%2 == 1}
zero2ten_odd_set
{1, 9, 25, 49, 81}
사전 조건제시법
조건제시법을 이용하여 사전을 생성하는 과정도 유사하다. 아래 코드는 0부터 10 사이의 홀수를 키로, 홀수의 제곱은 값으로 갖는 항목으로 구성된 사전을 생성한다.
odd_squares = {x : x**2 for x in range(11) if x%2 == 1}
odd_squares
{1: 1, 3: 9, 5: 25, 7: 49, 9: 81}
반면에 아래 코드는 문장에 포함된 단어를 키로, 단어의 길이를 값으로 갖는 항목들로 구성된 사전을 생성한다.
words = '파이썬은 범용 프로그래밍 언어입니다.'.split()
print(words)
['파이썬은', '범용', '프로그래밍', '언어입니다.']
len_dict = {k : len(k) for k in words}
len_dict
{'파이썬은': 4, '범용': 2, '프로그래밍': 5, '언어입니다.': 6}
13.4. 필수 예제#
참고: (필수 예제) 사전과 집합
13.5. 연습문제#
참고: (실습) 사전과 집합