13. 사전과 집합#

주요 내용

  • 집합과 사전

  • 해시 가능성

  • 형 변환 함수

  • 리스트/사전 조건제시법

  • 모음 자료형과 for 반복문

슬라이드

본문 내용을 요약한 슬라이드를 다운로드할 수 있다.

13.1. 사전#

사전 자료형은 keyvalue의 쌍으로 구성된 항목들의 집합이며, 딕셔너리dictionary 라고도 불린다. 예를 들어, 'Hello''World' 를 키로, '안녕', '세계' 를 각 키에 대한 값으로 갖는 사전은 다음과 같다.

{'Hello':'안녕', 'World':'세계'}

사전의 키와 해시 가능성

사전의 키는 앞서 집합과 관련해서 설명한 것과 동일한 이유로 정수, 부동소수점, 문자열, 튜플 등 해시 가능한 객체만 가능하다.

빈 사전

빈 사전은 아무것도 포함하지 않는 사전을 의미한다. 빈 사전를 만드는 방법은 아래와 같다.

empty_dict = {}
empty_dict
{}

다음 방식도 가능하다.

empty_dict = dict()
empty_dict
{}

13.1.1. 사전 연산#

in 연산자

사전의 키로 사용되었는 여부를 알려주는 논리 연산자다.

'city' in {"name": "강현", "age": "3"} 
False

in의 부정은 not in을 사용한다. 즉 키로 사용되지 않았는지 여부를 뭍는다.

'city' not in {"name": "강현", "age": "3"} 
True

len() 함수

사전에 포함된 항목의 개수를 반환한다.

len({"name": "강현", "age": "3"}) 
2

min()/max() 함수

키로 사용된 값들 중에서 최솟값/최댓값을 확인한다. 단, 키들의 기본적으로 자료형이 같고 크기 비교가 가능해야 한다.

max({1 : 'one', 2 : 'two', 3 : 'three'})
3
min({1 : 'one', 2 : 'two', 3 : 'three'})
1

13.1.2. 사전 인덱싱#

사전에 사용된 키를 이용한 인덱싱이 지원된다. 예를 들어, 아래 딕셔너리 dic에서 'Hello'에 대응하는 값을 확인하고자 하면
다음과 같이 대괄호를 사용하는 인덱싱을 이용한다.

dic = {'Hello' : '안녕', 'World' : '세계'}
dic['Hello']
'안녕'

존재하지 않는 키로 값을 추출하려고 하면, 오류가 발생한다.

>>> dic['Python']
KeyError                                  Traceback (most recent call last)
/tmp/ipykernel_1817/1517958361.py in <module>
----> 1 dic['Python']

KeyError: 'Python'

13.1.3. 항목의 추가와 업데이트#

아래와 같은 형식으로 사전에 항목을 추가하거나 업데이트 한다.

사전[key] = value

예를 들어, dicPython : 파이썬 항목을 다음과 같이 추가할 수 있다.

dic['Python'] = '파이썬'

dic
{'Hello': '안녕', 'World': '세계', 'Python': '파이썬'}

이미 사용된 키를 이용하면 키와 연결된 값이 업데이트 된다.

dic['World'] = '세상'
dic
{'Hello': '안녕', 'World': '세상', 'Python': '파이썬'}

13.1.4. 항목 삭제#

사전의 항목은 del 명령어를 사용하여 키를 지정하여 삭제한다.

del dic['World']
dic
{'Hello': '안녕', 'Python': '파이썬'}

13.1.5. 사전 메서드#

사전 자료형이 제공하는 주요 메서드는 아래와 같다.

메서드

설명

dict.keys()

사전에 사용된 key들을 모두 모아서 리스트와 비슷한 자료형을 만들어서 반환

dict.values()

사전에 사용된 value들을 모두 모아서 리스트와 비슷한 자료형을 만들어서 반환

dict.items()

사전에 사용된 item들을 모두 모아서 리스트와 비슷한 자료형으로 만들어서 반환

dict.get()

key에 대응되는 value를 반환. 존재하지 않는 key를 사용하면 None을 반환

dict.update()

다른 사전과 합함. 반환값은 None

dict.pop()

key에 해당하는 항목을 삭제. 반환값은 key에 대응하는 value. key가 존재하지 않으면 오류 발생.

dict.clear()

사전 안의 모든 항목들을 삭제. 반환값은 None

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', "해당 키가 없어요.")
'해당 키가 없어요.'

update() 메서드: 여러 항목 추가

dic.update({'Python' : '파이썬', 'Programming' : '프로그래밍'})
dic
{'Hello': '안녕', 'World': '세계', 'Python': '파이썬', 'Programming': '프로그래밍'}

pop() 메서드: 지정된 키 항목 삭제. 반환값은 키와 연관된 값

dic.pop('Python')
'파이썬'
dic
{'Hello': '안녕', 'World': '세계', 'Programming': '프로그래밍'}

clear() 메서드: 전체 항목 삭제

dic.clear()
dic
{}

13.1.6. 사전 연습문제#

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명 정보를 이용하여 키는 나이, 값은 해당 나이를 갖는 사람의 수를 사용하는 사전을 가리키는 age_count_dict를 선언하라.

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()

주의

빈 집합을 만들 때는 set()을 사용해야 한다. {}은 이어서 소개할 빈 사전empty dictionary을 가리킨다.

>>> a = {}
>>> type(a)
dict

13.2.1. 해시 가능성#

집합의 항목으로 리스트 등 가변 자료형은 사용할 수 없다.

>>> {[1, 3], 4}
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_1817/2739416386.py in <module>
----> 1 {[1, 3], 4}

TypeError: unhashable type: 'list'

이유는 가변 자료형이 해시 가능하지 않기 때문이다.

파이썬 객체의 해시 가능성은 hash() 함수의 인자로 사용될 수 있는가에 의해 결정된다. 즉 hash() 함수와 함께 호출됐을 때 오류가 발생하지 않아야 한다. 이렇게 hash() 함수의 인자로 사용될 수 있는 값을 해시 가능hashable하다고 부른다.

해시 가능한 인자에 대해 hash() 함수의 반환값은 특정 정수이며 서로 다른 인자에 대해 서로 다른 정수를 반환한다. 즉, 조금이라도 다른 해시 가능한 객체가 인자로 지정되면 다른 값을 계산한다.

hash('123')
-2942655733060632167
hash('123 ')
7692959542629320319
hash((1, 3, 4))
-1070212610609621906
hash((1, 2, 4))
-4363729961677198915

반면에 리스트 등 가변 자료형의 객체는 해시 가능하지 않다.

>>> hash([1, 3])
TypeError                                 Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_20092\1655639645.py in <module>
----> 1 hash([1, 3])

TypeError: unhashable type: 'list'

리스트를 포함한 튜플도 해시 가능하지 않다.

>>> hash(([1, 2], 3))
TypeError                                 Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_20092\587436505.py in <module>
----> 1 hash(([1, 2], 3))

TypeError: unhashable type: 'list'

집합에 해시 가능한 값만 포함될 수 있는 이유는 두 항목을 구별하기 위해 hash() 함수를 이용하기 때문이다. 반면에 리스트 등과 같은 가변 자료형의 값은 언제든 변할 수 있기에 정체를 제대로 파악할 수 없다.

13.2.2. 집합 연산#

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

min()/max() 함수

항목 중에서 최솟값/최댓값을 확인한다. 단, 기본적으로 모든 값의 자료형이 갖고 크기 비교가 가능해야 한다.

max({1, 3, 5, 7, 9})
9
min({1, 3, 5, 7, 9})
1

13.2.3. 집합 메서드#

집합 자료형이 제공하는 주요 메서드는 아래와 같다.

메서드

설명

set.union()

합집합 반환

set.intersection()

교집합 반환

set.difference()

차집합 반환

set.issubset()

부분 집합 여부를 판단하여 반환

set.add()

항목추가. 반환값은 None

set.remove()

특정 항목 삭제. 반환값은 None

set.pop()

임의의 항목 삭제. 반환값은 삭제하는 항목. 공집합에 사용하면 오류 발생

set.clear()

모든 항목을 삭제. 반환값은 None

set.update()

여러 개의 항목 한번에 추가. 반환값은 None

union() 메서드: 합집합

{1, 2}.union({3, 4})
{1, 2, 3, 4}

intersection() 메서드: 교집합

{1, 2, 3}.intersection({3, 4})
{3}

difference() 메서드: 집합 뺄셈

{1, 2, 3}.difference({3, 4})
{1, 2}

issubset() 메서드: 부분집합 관계

{1, 2, 3}.issubset({3, 4})
False
{1, 2, 3}.issubset({1, 2, 3, 4})
True

add() 메서드: 항목 추가

a_set = {1, 2, 3, 4}
a_set.add(8)
a_set
{1, 2, 3, 4, 8}

remove() 메서드: 지정된 항목 제거

a_set.remove(1)
a_set
{2, 3, 4, 8}

pop() 메서드: 임의 항목 제거

a_set.pop()
2
a_set
{3, 4, 8}

clear() 메서드: 모든 항목 제거

a_set.clear()
a_set
set()

update() 메서드: 여러 항목 추가

a_set.update({5, 10, 15})
a_set
{5, 10, 15}

13.3. 형변환 함수#

list() 함수

임의의 모음 자료형을 리스트로 변환한다.

list('abc')
['a', 'b', 'c']
list((1, 2, 3))
[1, 2, 3]
list({1, 2, 5})
[1, 2, 5]

사전은 여러 방식으로 변환된다.

dic = {'Hello' : '안녕', 'World' : '세계'}
  • 키 리스트

list(dic)
['Hello', 'World']
list(dic.keys())
['Hello', 'World']
  • 값 리스트

list(dic.values())
['안녕', '세계']
  • (키, 값) 리스트

list(dic.items())
[('Hello', '안녕'), ('World', '세계')]

tuple() 함수

임의의 모음 자료형을 튜플로 변환한다.

tuple('abc')
('a', 'b', 'c')
tuple([1, 2, 3])
(1, 2, 3)
tuple({1, 2, 5})
(1, 2, 5)

사전에 대해서는 list() 함수와 동일한 방식으로 작동한다.

dic = {'Hello' : '안녕', 'World' : '세계'}
tuple(dic)
('Hello', 'World')
tuple(dic.keys())
('Hello', 'World')
tuple(dic.values())
('안녕', '세계')
tuple(dic.items())
(('Hello', '안녕'), ('World', '세계'))

set() 함수

임의의 모음 자료형을 집합으로 변환한다. 이 과정에서 순서와 중복이 무시된다.

set('abc')
{'a', 'b', 'c'}
set([1, 1, 2, 5])
{1, 2, 5}
set((1, 3, 3, 9, 1))
{1, 3, 9}
dic = {'Hello' : '안녕', 'World' : '세계'}
set(dic)
{'Hello', 'World'}
set(dic.keys())
{'Hello', 'World'}
set(dic.values())
{'세계', '안녕'}
set(dic.items())
{('Hello', '안녕'), ('World', '세계')}

dict() 함수

키-값의 튜플 형식의 항목을 갖는 모음 자료형을 사전으로 변환한다. 단, 키로 사용되는 값은 해시 가능해야 한다.

data = [('Hello', '안녕'), ('World', '세계'), ('Programming', '프로그래밍')]
dict(data)
{'Hello': '안녕', 'World': '세계', 'Programming': '프로그래밍'}

zip() 함수를 활용하면 편리하다.

data = zip('abcde', [1, 2, 3, 4, 5])
dict(data)
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

13.4. 조건제시법#

조건제시법comprehension을 이용하여 리스트, 집합, 사전을 정의할 수 있다.

리스트 조건제시법

수학에서 0과 10사이에 있는 홀수들의 제곱을 원소로 갖는 집합을 조건제시법으로 표현하면 다음과 같다.

\[\{ x^2 \mid 0 \le x \le 10, \text{ 단 $x$는 홀수} \}\]

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}

사전 조건제시법

조건제시법을 이용하여 사전을 생성하는 과정도 유사하다. 아래 코드는 문장에 포함된 단어를 키로, 단어의 길이를 값으로 갖는 항목들로 구성된 사전을 생성한다.

words = '파이썬은 범용 프로그래밍 언어입니다.'.split()
words
['파이썬은', '범용', '프로그래밍', '언어입니다.']

words 에 포함된 단어와 단어의 길이로 짝을 이룬 항목으로 구성된 사전은 다음과 같다.

len_dict = {k : len(k) for k in words}
len_dict
{'파이썬은': 4, '범용': 2, '프로그래밍': 5, '언어입니다.': 6}

아래 코드는 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}

13.5. 모음 자료형과 for 반복문#

앞서 소개된 모든 모음 자료형은 for 반복문과 함께 사용될 수 있다.

  • 문자열과 반복문

aString = "Python is lovely!"

for chr in aString:
    print(chr, end=' ')
P y t h o n   i s   l o v e l y ! 
  • 리스트와 반복문

aList = list(aString)
print(aList)
['P', 'y', 't', 'h', 'o', 'n', ' ', 'i', 's', ' ', 'l', 'o', 'v', 'e', 'l', 'y', '!']
for item in aList:
    print(item, end=' ')
P y t h o n   i s   l o v e l y ! 
  • 튜플과 반복문

aTuple = tuple(aString)
print(aTuple)
('P', 'y', 't', 'h', 'o', 'n', ' ', 'i', 's', ' ', 'l', 'o', 'v', 'e', 'l', 'y', '!')
for item in aTuple:
    print(item, end=' ')
P y t h o n   i s   l o v e l y ! 
  • 집합과 반복문

aSet = set(aString)
print(aSet)
{'P', 't', '!', 'y', 'n', 'h', 'o', 'v', ' ', 'l', 'i', 's', 'e'}

집합은 중복을 허용하지 않음에 주의하라.

for item in aSet:
    print(item, end=' ')
P t ! y n h o v   l i s e 
  • 사전과 반복문

words = 'Python is a general purpose language'.split()
len_dict = {k : len(k) for k in words}
len_dict
{'Python': 6, 'is': 2, 'a': 1, 'general': 7, 'purpose': 7, 'language': 8}

사전에 대한 반복문은 키key에 대해 실행된다.

for item in len_dict:
    print(item, end=' ')
Python is a general purpose language 

키와 값의 쌍에 대해 반복문을 실행하려면 items() 메서드를 이용한다. 단, 키와 값 각각에 대해 변수를 지정하는 게 좋다.

for key, value in len_dict.items():
    print(f"{key:>8} 키의 값: {value}")
  Python 키의 값: 6
      is 키의 값: 2
       a 키의 값: 1
 general 키의 값: 7
 purpose 키의 값: 7
language 키의 값: 8

항목을 쪼개서 사용할 수도 있다.

for item in len_dict.items():
    key = item[0]
    value = item[1]
    print(f"{key:>8} 키의 값: {value}")
  Python 키의 값: 6
      is 키의 값: 2
       a 키의 값: 1
 general 키의 값: 7
 purpose 키의 값: 7
language 키의 값: 8

값에 대해 반복문을 실행하려면 values() 메서드를 이용한다.

for item in len_dict.values():
    print(item, end=' ')
6 2 1 7 7 8 
  • zip 과 반복문

aZip = zip("abcdefgh",(1, 2, 3, 4, 5), [5, 10, 15])

for item in aZip:
    print(item)
('a', 1, 5)
('b', 2, 10)
('c', 3, 15)

항목별로 변수를 지정하여 활용할 수도 있다.

aZip = zip("abcdefgh",(1, 2, 3, 4, 5), [5, 10, 15])

for chr, tup, lst in aZip:
    print(f"({chr}, {tup}, {lst})")
(a, 1, 5)
(b, 2, 10)
(c, 3, 15)
  • range 와 반복문

for item in range(1, 10, 2):
    print(f"{item}의 제곱: {item**2}")
1의 제곱: 1
3의 제곱: 9
5의 제곱: 25
7의 제곱: 49
9의 제곱: 81

13.6. 연습문제#

참고: (실습) 모음 자료형 2부: 집합, 사전, range, 조건제시법