Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

12 사전과 집합

Updated: 01 jun 2026

두 개의 비순차 자료형 사전과 집합을 소개한다. 비순차 자료형은 문자열, 리스트, 튜플과는 달리 순서를 따지지 않으며 항목의 중복도 허용하지 않는다.

12.1사전

사전은 keyvalue의 쌍으로 구성된 항목들로 구성된 모음 자료형이며, 딕셔너리dictionary 라고도 불린다.

예를 들어, 'Hello':'안녕''Hello'를 키로, '안녕'을 값으로 갖는 쌍이며, 'world':'세계''world'를 키로, '세계'를 값으로 갖는 쌍이다. 두 쌍을 항목으로 갖는 사전은 다음과 같이 두 쌍을 중괄호로 감싼다.

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

빈 사전

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

empty_dict = {}
empty_dict
{}

다음 방식도 빈 사전을 생성한다.

empty_dict = dict()
empty_dict
{}

dict 자료형

사전은 dict 자료형의 값이다.

type({'Hello':'안녕', 'world':'세계'})
dict

dict() 함수

dict() 함수는 키와 값의 튜플 형식의 항목으로 구성된 모음 자료형을 사전 자료형으로 변환한다.

예를 들어, 아래 코든느 튜플로 구성된 리스트를 사전으로 변환한다. 튜플의 첫째 항목은 키로, 둘째 항목은 값으로 사용된다.

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

zip() 함수와 dict() 함수를 조합하여 편리하게 사전을 생성할 수 있다.

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

12.1.1사전의 키 자료형

사전의 키로 정수, 부동소수점, 문자열, 튜플 등 불변 자료형만 허용된다. 즉, 리스트와 같은 가변 자료형은 사전의 키로 사용될 수 없다. 반면에 키에 대응하는 값은 임의의 자료형이 허용된다.

  • 정수, 부동소수점 키

dic1 = {2: '정수', 1.2 : '부동소수점'}
dic1
{2: '정수', 1.2: '부동소수점'}
  • 문자열 키

dic2 ={'a':1, 'b':2, 'c':3}
dic2
{'a': 1, 'b': 2, 'c': 3}
  • 튜플 키

dic3 = {(1, 'a') : [1, 2, 3], (2, 'b') : [4, 5, 6]}
dic3
{(1, 'a'): [1, 2, 3], (2, 'b'): [4, 5, 6]}
  • 리스트 키?

리스트를 키로 사용할 수는 없다는 의미에서 TypeError가 발생한다.

dic4 = {[1, 2] : 'a'}
dic4
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[9], line 1
----> 1 dic4 = {[1, 2] : 'a'}
      2 dic4

TypeError: unhashable type: 'list'
  • 리스트를 포함하는 튜플은?

리스트와 같은 가변 자료형은 어떤 형식으로도 키에 포함되어서는 안된다. 아래 코드는 튜플을 키로 사용하지만 키의 첫째 항목이 리스트이기에 허용되지 않는다.

dic4 = {([1, 2], '가') : 'ABC'}
dic4
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[10], line 1
----> 1 dic4 = {([1, 2], '') : 'ABC'}
      2 dic4

TypeError: unhashable type: 'list'

12.1.2사전 연산

문자열, 리스트, 튜플과는 다르게 사전 연산은 특별히 없다. 다만 항목 포함 여부와 항목의 개수를 확인해주는 in 연산자와 len() 함수 정도만 활용된다.

in 연산자

사전의 키로 사용되었는 여부를 알려주는 논리 연산자다. 예를 들어, 아래 코드는 'city' 문자열이 사전의 키로 사용되었는지 여부를 판별한다.

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

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

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

len() 함수

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

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

12.1.3사전 인덱싱

사전 인덱싱은 키를 활용한다. 사전은 비순차 자료형이기에 정수 인덱스를 지원하지 않으며 따라서 정수 인덱싱도 사용할 수 없다.

예를 들어, 아래 딕셔너리 dic에서 'Hello' 문자열을 키로 갖는 항목의 값을 확인하고자 하면 다음과 같이 대괄호 안에 'Hello'를 지정한다.

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

만약에 키로 사용되지 않는 값으로 인덱싱을 시도하면 지정된 키가 없다는 KeyError 오류가 발생한다.

dic['Python']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[15], line 1
----> 1 dic['Python']

KeyError: 'Python'

사전은 가변 자료형이다. 즉, 사전의 항목을 추가, 업데이트, 삭제할 수 있다.

사전 항목 추가, 업데이트

사전에 항목을 추가하거나 기존 항목을 업데이트는 사전 인덱싱 형식으로 이루어진다.

사전[key] = value

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

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

만약에 키가 이미 사용되었다면 키와 연결된 값이 업데이트 된다. 예를 들어 아래 코드는 'world' 키의 값의 '세계'에서 '세상'으로 업데이트한다.

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

사전 항목 삭제

사전의 항목은 del 명령어에 키를 지정하여 삭제할 수 있다. 아래 코드는 'world' 가 키로 사용된 항목을 삭제한다.

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

사용되지 않은 키를 지정해서 항목 삭제를 시도하면 해당 키를 갖는 항목이 없다는 KeyError 오류가 발생한다.

del dic['World']
dic
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[19], line 1
----> 1 del dic['World']
      2 dic

KeyError: 'World'

12.1.4사전 메서드

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

Table 1:사전 주요 메서드

기능

메서드

설명

키 확인

keys()

사전에 사용된 키로 구성된 순차 자료형 값 반환

값 확인

values()

사전에 사용된 값으로 구성된 순차 자료형 값 반환

키와 값 확인

items()

사전에 사용된 키와 값의 순서쌍으로 구성된 순차 자료형 값 반환

값 반환

get()

지정된 키에 대한 값 반환

언급된 메서드의 기능을 설명을 위해 아래 사전을 이용한다.

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

keys() 메서드

사전에 사용된 키들로 이루어진 dict_keys라는 모음 자료형을 반환한다. dict_keys 자료형의 값은 리스트는 아니지만 반복문 등에서 유사하게 활용될 수 있다.

dic.keys()
dict_keys(['Hello', 'World'])

values() 메서드

사전에 사용된 값들으로 이루어진 dict_values라는 모음 자료형을 반환한다. 반환값은 역시 반복문 등에서 리스트처럼 유용하게 활용된다.

dic.values()
dict_values(['안녕', '세계'])

items() 메서드

사전에 사용된 키와 값의 쌍으로 이루어진 dict_items라는 모음 자료형을 반환한다. 반환값은 (키, 값) 형식의 항목으로 구성된 리스트처럼 반복문 등에서 활용될 수 있다.

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

get() 메서드

사전 인덱싱과 유사하게 작동한다. 즉, 메서드의 인자로 지정된 키에 해당하는 값을 반환한다. 아래 코드는 dic['Hello']와 동일한 값을 반환한다.

dic.get('Hello')
'안녕'

사전 인덱싱과는 다르게 메서드의 인자가 키로 사용되지 않았다 하더라도 오류가 발생하지는 않고 대신 None을 반환한다.

print(dic.get('hello'))
None

get() 메서드의 둘째 인자를 지정하면 사전의 키로 사용되지 않은 인자에 대해 None이 아닌 둘째 인자를 대신 반환한다.

dic.get('hello', "해당 키가 없어요.")
'해당 키가 없어요.'

12.1.5사전 반복문

사전에 대한 반복문은 키에 대해 실행된다. 아래 코드는 data_dict 사전에 사용된 키들만을 화면에 줄바꿈 없이 출력한다.

data_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

for item in data_dict:
    print(item, end=' ')
a b c d e 

items() 메서드 활용

키와 값의 쌍에 대해 반복문을 실행하려면 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() 메서드 활용

아래 코드처럼 값만을 이용하려면 values() 메서드와 함께 반복문을 싱행한다.

for item in data_dict.values():
    print(item, end=' ')
1 2 3 4 5 

12.1.6예제

예제 1

아래 코드를 실행할 때 발생하는 오류를 설명하라.

dic = {([1, 2], 3): 'a'}
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[31], line 1
----> 1 dic = {([1, 2], 3): 'a'}

TypeError: unhashable type: 'list'

답:

사전의 키로 튜플 ([1, 2], 3)이 사용되었지만 튜플의 첫째 항목이 리스트이다. 그런데 리스트와 같은 가변 자료형은 키에 어떤 형식으로 포함되지 않아야 한다.

예제 2

정수들의 리스트가 다음과 같이 주어졌다.

num_list = [2, 5, 2, 3, 3, 2]

(1) num_list에서 정수 2가 항목으로 등장하는 모든 인덱스로 구성된 리스트를 가리키는 idx_2 변수를 정의하는 코드를 작성하라.

힌트: enumerate() 함수와 for 반복문 활용

답:

enumerate() 함수를 리스트에 적용하면 항목과 인덱스의 쌍으로 구성된 모음 자료형이 생성되고, 이 점을 활용하여 for 반복문을 실행하면 항목과 인덱스의 관계가 확인된다.

for idx, item in enumerate(num_list):
    print(f"{idx} 번째 요소: {item}")
0 번째 요소: 2
1 번째 요소: 5
2 번째 요소: 2
3 번째 요소: 3
4 번째 요소: 3
5 번째 요소: 2

유사한 방식으로 정수 2가 위치한 곳의 인덱스만 추출하여 새로운 리스트를 생성하면 된다.

idx_list = []
for idx, item in enumerate(num_list):
    if item == 2:
        idx_list.append(idx)

print(idx_list)
[0, 2, 5]

(2) num_list에 포함된 항목을 인자로 받아, 그 항목이 등장하는 모든 인덱스를 리스트로 반환하는 idx_list_func() 함수를 정의하라.

답:

이전 문제의 코드를 함수의 본문으로 사용하기 위해 함수의 매개변수와 반환값만 지정하면 된다.

  • 함수 매개변수: 정수 2를 매개변수화 해야 함

  • 함수 반환값: idx_list를 반환값으로 지정

def idx_list_func(target):
    idx_list = []
    for idx, item in enumerate(num_list):
        if item == target:
            idx_list.append(idx)
    return idx_list

정수 2가 위치한 인데스들의 리스트는 다음과 같다.

idx_list_func(2)
[0, 2, 5]

정수 5가 위치한 인데스들의 리스트는 다음과 같다.

idx_list_func(5)
[1]

(3) 이전 문제의 idx_list_func() 함수를 수정하여 두 개의 인자를 받도록 하라.

  • 첫째 매개변수 num_list: 임의의 리스트

  • 둘째 매개변수 target: num_list의 항목

함수 반환값은 num_list에서 target이 등장하는 모든 인덱스로 구성된 리스트이다.

답:

이전 문제의 idx_list_func() 함수에서 num_list는 고정된 리스트였는데 이를 매개 변수로 바꾸면 임의의 리스트에 대해 함수가 동일한 방식으로 작동한다.

def idx_list_func(num_list, target):
    idx_list = []
    for idx, item in enumerate(num_list):
        if item == target:
            idx_list.append(idx)
    return idx_list

함수 호출을 위해 이제는 num_list 매개 변수에 대한 인자도 지정해야 한다.

idx_list_func(num_list, 2)
[0, 2, 5]

다른 리스트를 인자로 지정해도 된다.

num_list_2 = list('abcdeccea')
num_list_2
['a', 'b', 'c', 'd', 'e', 'c', 'c', 'e', 'a']
idx_list_func(num_list_2, 'c')
[2, 5, 6]

예제 3

다음 info_list는 6명의 이름, 전화번호, 나아. 키, 출생지 정보를 저장한 중첩 리스트를 가리킨다.

info_list = [['김강현', '010-1234-5678', 20, 172.5, '제주'],
             ['황현', '02-9871-1234', 19, 163.5, '서울'],
             ['남세원', '010-3456-7891', 21, 156.7, '경기'],
             ['최흥선', '070-4321-1111', 21, 187.2, '부산'],
             ['김현선', '010-3333-8888', 22, 164.6, '광주'],
             ['함중아', '010-7654-2345', 18, 178.3, '강원']]

(1) info_list를 이용하여 아래와 같이 이름을 키로, 나머지 정보들로 구성된 리스트는 값으로 갖는 사전을 가리키는 info_dict 변수를 정의하는 코드를 작성하라.

{'김강현': ['010-1234-5678', 20, 172.5, '제주'],
 '황현': ['02-9871-1234', 19, 163.5, '서울'],
 '남세원': ['010-3456-7891', 21, 156.7, '경기'],
 '최흥선': ['070-4321-1111', 21, 187.2, '부산'],
 '김현선': ['010-3333-8888', 22, 164.6, '광주'],
 '함중아': ['010-7654-2345', 18, 178.3, '강원']}

힌트: 리스트 해체, for 반복문, 사전 항목 추가

답:

6명 각자의 이름을 키로, 나머지 정보로 구성된 리스트를 값으로 사용하는 사전을 구성한다. 이를 위해 for 반복문과 리스트 해체를 이용한다. 먼저 info_list의 항목은 아래 형식임에 주의한다.

person = ['김강현', '010-1234-5678', 20, 172.5, '제주']

위 리스트를 이름과 나머지로 구분하기 위해 다음과 같이 리스트 해체를 이용한다.

name, *info = person

그러면 name은 이름을, info는 나머지 항목으로 구성된 리스트를 가리키게 되어 두 변수를 다음과 같이 선언한 것과 동일하다.

name = person[0]
info = person[1:]

이제 for 반복문을 info_list에 대해 실행하면서 아래 형식으로 항목을 하나씩 추가하면 간단하게 info_dict 변수를 아래와 같이 선언할 수 있다.

info_dict[name] = info

info_dict = dict()

for person in info_list:
    name, *info = person
    info_dict[name] = info

info_dict
{'김강현': ['010-1234-5678', 20, 172.5, '제주'], '황현': ['02-9871-1234', 19, 163.5, '서울'], '남세원': ['010-3456-7891', 21, 156.7, '경기'], '최흥선': ['070-4321-1111', 21, 187.2, '부산'], '김현선': ['010-3333-8888', 22, 164.6, '광주'], '함중아': ['010-7654-2345', 18, 178.3, '강원']}

(2) info_dict를 이용하여 이름을 지정하면 전화번호를 반환하는 함수 info_tel() 함수를 정의하라.

힌트: 사전 인덱싱, 리스트 인덱싱 활용

답:

사전 인덱싱은 마치 하나의 함수처럼 작동한다. 예를 들어, 아래 코드는 김현선의 정보를 확인해준다.

info_dict['김현선']
['010-3333-8888', 22, 164.6, '광주']

김현선의 전화번호는 위 결과인 리스트에 대해 정수 인덱싱을 활용하면 바로 알 수 있다.

info_dict['김현선'][0]
'010-3333-8888'

따라서 info_tel() 함수를 다음과 같이 정의할 수 있다.

def info_tel(name):
    return info_dict[name][0]
info_tel('김현선')
'010-3333-8888'

(3) info_tel() 함수를 get() 사전 메서드의 반환값을 활용하도록 함수를 재정의하라. 단, info_dict 사전의 키로 사용되지 않은 경우 '정보 없음!' 문자열을 반환해야 한다.

답:

이전 정의에서 info_dict[name] 표현식을 info_dict.get(name) 표현식으로 대체하면 일단 절대로 오류가 발생하지 않는다. 그리고 if조건문을 활용하여 info_dict.get(name)의 반환값이 None인 경우 '정보 없음!' 문자열을 반환하도록 하면 된다.

아래 코드에서 if infoinfo가 가리키는 값이 None이 아닌 경우에 해당한다.

def info_tel(name):
    info = info_dict.get(name)
    if info:
        return info[0]
    else:
        return "정보 없음!"
  • 정보가 있는 경우

info_tel('김현선')
'010-3333-8888'
  • 정보가 없는 경우

info_tel('강남길')
'정보 없음!'

예제 4

이전 예제에서 정의된 info_dict 변수는 다음과 같다.

info_dict = {'김강현': ['010-1234-5678', 20, 172.5, '제주'],
             '황현': ['02-9871-1234', 19, 163.5, '서울'],
             '남세원': ['010-3456-7891', 21, 156.7, '경기'],
             '최흥선': ['070-4321-1111', 21, 187.2, '부산'],
             '김현선': ['010-3333-8888', 22, 164.6, '광주'],
             '함중아': ['010-7654-2345', 18, 178.3, '강원']}

for 반복문을 이용하여 모든 사람의 나이를 한 살씩 증가시켜라.

힌트: 사전 values() 메서드, 리스트 인덱싱 활용

답:

예를 들어 김강현의 나이는 다음과 같다.

info_dict['김강현'][1]

따라서 김강현의 나이를 다음과 같이 1살 증가시킬 수 있다.

info_dict['김강현'][1] += 1

아래 코드는 설명된 방식을 모든 사람들에 적용하여 나이를 한 살씩 증가시키게 된다.

for info in info_dict.values():
    info[1] += 1

info_dict
{'김강현': ['010-1234-5678', 21, 172.5, '제주'], '황현': ['02-9871-1234', 20, 163.5, '서울'], '남세원': ['010-3456-7891', 22, 156.7, '경기'], '최흥선': ['070-4321-1111', 22, 187.2, '부산'], '김현선': ['010-3333-8888', 23, 164.6, '광주'], '함중아': ['010-7654-2345', 19, 178.3, '강원']}

12.1.7연습문제

문제 1

아래 코드를 실행할 때 발생하는 오류를 설명하라.

dic5 = {[('abc', 7), (1, 2)]:'a'}
dic5
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[53], line 1
----> 1 dic5 = {[('abc', 7), (1, 2)]:'a'}
      2 dic5

TypeError: unhashable type: 'list'

문제 2

영어 단어는 키로, 단어의 뜻은 값으로 하는 사전이 다음과 같이 주어졌다.

eng_dict = {'dog':'개', 
            'cat':'고양이',
            'lion':'사자',
            'tiger':'호랑이',
            'snake':'뱀'}

영어 단어가 인자로 입력되었을 때 사전의 키로 사용되었다면 뜻을 반환하고, 아니면 아래 문장을 반환하는 eng_fun() 함수를 선언하라. 단, 대소문자는 구분하지 않는다.

찾는 단어가 없습니다.

힌트: 문자열 lower() 메서드, 사전 in 연산자, 사전 인덱싱 활용

문제 3

아래 item_price 에는 음료와 과자의 가격이 들어 있다.

item_price = "커피-1050원, 우유-870원, 밀크티-1300원, 새우과자-950원, 감자칩-1100원"

(1) 아래 사전 객체를 가리키는 변수 price_dict를 정의하는 코드를 작성하라.

{'커피' : 1050, '우유' : 870, '밀크티' : 1300, '새우과자' : 950, '감자칩' : 1100}

힌트: 문자열 split() 메서드, 리스트 해체 활용

(2) price_dict를 이용하여 지급 액수와 구매할 물품을 인자로 입력받았을 때 구매가격과 찾는 물품에 따라 다음과 같이 출력하는 vending_machine() 함수를 선언하라.

잔돈: 300원
부족 액수: 500원
찾는 물품: 없음

힌트: if 조건문, 사전 인덱싱, f-문자열 활용

문제 4

어느 유료 주차장의 주차 요금은 아래와 같으며, 1일 최대 주차 요금은 4만원으로 책정된다.

  • 최초 30분: 2,000원

  • 30분 초과 후 10분마다: 500원

  • 1일(24시간) 최대: 40,000원

아래 car_in_out 변수는 일반차량에 대한 입출차 기록이 아래 형식으로 저장된 리스트를 가리킨다.

입출차 시간 차량번호네자리 입출차여부
car_in_out = ['07:30 1234 IN', 
              '07:35 2580 IN',
              '08:15 0328 IN',
              '08:45 2580 OUT',
              '08:55 9876 IN',
              '11:00 1597 IN',
              '15:15 1234 OUT',
              '21:00 0328 OUT',
              '23:45 9876 OUT']

(1) 차량 번호 네 자리가 주어졌을 해당 차량의 입출차 시간을 항목으로 갖는 사전을 반환하는 car_parking_info() 함수를 정의하라. 예를 들어, 1234 차량의 경우 다음 사전이 반환되어야 한다.

{'IN': '07:30', 'OUT': '15:15'}

(2) 입차 시간과 출차 시간이 '07:30' 형식의 문자열로 주어졌을 때 분 단위의 주차 시간을 반환하는 parking_duration() 함수를 정의하라.

(3) 분 단위의 주차 시간이 주어졌을 때 주차 요금을 반환하는 parking_fee() 함수를 정의하라.

(4) 차량 번호가 주어지면 주차 비용을 반환하는 car_parking_fee() 함수를 정의하라.

힌트: 이전 문제들의 함수 활용

12.2집합

집합 자료형은 수학에서 다루는 집합처럼 작동하도록 만든 비순차 모음 자료형이며 항목들을 중괄호로 감싼다. 집합은 항목의 중복을 허용하지 않고, 항목들 사이의 순서 또한 무시한다.

예를 들어, 아래 코드는 네 개의 항목을 포함한 집합을 정의한다. 정수 4가 두 번 사용되었지만 하나로 취급되며, 항목들의 순서도 무신됨을 잘 보여준다.

a_set = {4, 4, 9.2, "apple", True, 4}
a_set
{4, 9.2, True, 'apple'}

집합의 자료형은 set이다.

type(a_set)
set

공집합

공집합empty set은 아무것도 포함하지 않는 집합을 의미한다. 공집합를 만드는 방법은 아래와 같다.

empty_set = set()
empty_set
set()

주의사항

{}은 공집합이 아니라 빈 사전, 즉 dict 자료형의 값임에 주의한다.

a = {}
type(a)
dict

set() 함수

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

set([5, 1, 1, 2, 5, 5, 1])
{1, 2, 5}

12.2.1집합 활용법

프로그래밍에서 집합은 주로 순차 자료형의 항목에서 중복을 제거하고 싶을 때 사용한다. 이때 set() 함수가 매우 유용하다.

예를 들어 아래 코드는 리스트에서 중복된 항목이 삭제된, 하지만 동일한 항목을 포함한 새로운 리스트를 생성한다. 단, 기존 리스트에서 사용된 항목들의 순서는 보존되지 않음에 주의한다.

list(set([5, 1, 1, 2, 5, 5, 1]))
[1, 2, 5]

12.2.2집합 연산

튜플 연산은 사전의 경우와 유사하게 작동한다.

in 연산자

집합의 항목(원소)으로 등장하는지 여부를 알려주는 논리 연산자다.

1 in {1, 2, 3, 9, 4}
True
'a' not in {1, 'b', True, 9}
True

len() 함수

집합에 포함된 항목의 개수를 반환한다. 단, 중복은 허용하지 않는다.

len({1, 3, 5, 5, 7, 5, 7, 9})
5

12.2.3예제

예제 1

리스트를 인자로 받아 사용된 항목의 개수를 반환하는 함수 count_elem()를 구현하라. 단, 중복 항목은 하나로 간주한다.

count_elem([2, 5, 2, 3, 3, 8, 2, 7]) = 5
count_elem([15, 3, 15, 1, 3]) = 3

답:

리스트를 집합으로 변환한 후에 항목의 개수를 세면 된다.

def count_elem(xs):
    return len(set(xs))

print(count_elem([2, 5, 2, 3, 3, 8, 2, 7]))
print(count_elem([15, 3, 15, 1, 3]))
5
3

예제 2

다음 info_dict 변수는 6명의 전화번호, 나이, 키, 출생지 정보를 담은 사전을 가리킨다.

info_dict = {'김강현': ['010-1234-5678', 20, 172.5, '제주'],
             '황현': ['02-9871-1234', 19, 163.5, '서울'],
             '남세원': ['010-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로부터 각각의 사람에 대해 나이를 키로, 해당 나이를 갖는 사람의 수를 값으로 갖는 항목으로 구성된 사전을 가리키는 age_count_dict를 정의하는 코드를 작성하라. 즉, 아래 형식의 사전을 가리켜야 한다.

{18: 1, 19: 1, 20: 1, 21: 2, 22: 1}

답:

먼저 나이로 구성된 리스트를 생성한다.

ages = []

for person_info in info_dict.values():
    ages.append(person_info[1])

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}

예제 3

다음과 같이 방문 기록이 리스트로 주어졌다.

visits = ['home', 'search', 'product', 'home', 'cart', 'search', 'payment']

visits에 등장하는 항목을 처음 등장한 순서대로, 단 중복 없이 담은 리스트 unique_visits를 생성하라. 실행 결과는 다음과 같아야 한다.

['home', 'search', 'product', 'cart', 'payment']

힌트:

  • for 반복문, in 연산자, 집합 add() 메서드, 리스트 append() 메서드 활용

  • 집합 add() 메서드는 기존 집합에 항목을 추가함.

답:

리스트의 모든 항목에 대해 반복문을 실행하면서 기존에 방문하지 않았던 사이트만 항목으로 담은 리스트를 생성한다. 한 번 이상 확인된 항목은 모두 seen 집합에 저장하여 새로운 항목을 확인할 때 이전에 확인된 항목인지 여부를 판별한다.

visits = ['home', 'search', 'product', 'home', 'cart', 'search', 'payment']

seen = set()              # 방문한 페이지를 추적하기 위한 집합
unique_visits = []        # 중복 없이 방문한 페이지를 저장할 리스트

for visit in visits:
    if visit not in seen: # 기존에 방문한 페이지가 아닌 경우에만 unique_visits 리스트에 추가
        seen.add(visit)
        unique_visits.append(visit)

unique_visits
['home', 'search', 'product', 'cart', 'payment']

12.2.4연습문제

문제 1

어느 반 학생들의 이름이 리스트로 주어졌다.

students = ['Apeach', 'Ryan', 'Muzi', 'Choonsik', 'Neo', 'Tube', 'Choonsik']

아래 사전처럼 번호와 이름을 각각 키와 값으로 갖는 사전을 가리키는 변수 students_dict를 정의하는 코드를 작성하라. 단, 번호는 이름순으로 부여되고, 동명이인은 무시하도록 한다.

{1: 'Apeach', 2: 'Choonsik', 3: 'Muzi', 4: 'Neo', 5: 'Ryan', 6: 'Tube'}

힌트: set() 함수, sorted() 함수, enumerate() 함수 활용

문제 2

리스트를 내림차순으로 정렬한 리스트를 반환하는 함수 sort_elem()를 구현하라. 단, 중복 항목은 무시해야 한다.

예를 들어 아래와 같이 함수가 작동해야 한다.

sort_elem([2, 5, 2, 3, 3, 8, 2, 7]) = [8, 7, 5, 3, 2]
sort_elem([15, 3, 15, 1, 3]) = [15, 3, 1]

힌트: sorted() 함수 활용

문제 3

다음과 같이 주문 번호가 리스트로 주어졌다.

orders = ['A102', 'A105', 'A102', 'A107', 'A105', 'A110', 'A102']

두 번 이상 등장한 주문 번호를 처음 중복이 확인된 순서대로 담은 리스트 duplicated_orders를 생성하는 코드를 작성하라. 코드 실행 결과는 다음과 같아야 한다.

['A102', 'A105']

힌트:

  • for 반복문, in 연산자, 집합 add() 메서드, 리스트 append() 메서드 활용

  • 집합 add() 메서드는 기존 집합에 항목을 추가함.

12.3조건제시법

조건제시법comprehension은 기존의 여러 값으로부터 일정한 규칙이나 조건을 만족하는 값들을 골라 새로운 모음 자료형을 생성하는 표현식이며, 컴프리헨션, 또는 내포라고 불리기도 한다. 조건제시법을 이용하여 리스트, 집합, 사전, 튜플 등을 정의할 수 있다.

12.3.1집합 조건제시법

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

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

0과 10 사이에 있는 홀수들의 제곱을 항목으로 갖는 집합을 다음과 같이 정의할 수 있다.

zero2ten_odd_set = {x**2 for x in range(11) if x%2 == 1}
zero2ten_odd_set
{1, 9, 25, 49, 81}

위 코드는 실제로는 다음 코드와 동일하게 파이썬 내부에서 실행된다. 아래 코드에서 add()는 집합에 항목을 추가하는 집합 메서드이다.

zero2ten_odd_set = set()

for x in range(11):
    if x % 2 == 1:
        zero2ten_odd_set.add(x**2)

zero2ten_odd_set
{1, 9, 25, 49, 81}

if 조건문의 역할

if 조건문을 필요에 따라 사용하지 않아도 된다. 아래 코드는 0부터 10 사이의 모든 정수들의 제곱으로 구성된 집합을 생성한다.

zero2ten_set = {x**2 for x in range(11)}
zero2ten_set
{0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100}

12.3.2리스트 조건제시법

조건제시법으로 0부터 10 사이에 있는 홀수들의 제곱을 항목으로 갖는 리스트를 집합 조건제시법과 사실상 동일하게 생성할 수 있다.

zero2ten_odd_list = [x**2 for x in range(11) if x%2 == 1]
zero2ten_odd_list
[1, 9, 25, 49, 81]

위 코드를 반복문으로 구현하면 다음과 같다.

zero2ten_odd_list = []

for x in range(11):
    if x%2 == 1:
        zero2ten_odd_list.append(x**2)

zero2ten_odd_list
[1, 9, 25, 49, 81]

아래 코드는 if 조건문을 사용하지 않아서 최종적으로 0부터 10 사이의 모든 정수들의 제곱으로 구성된 리스트를 생성한다.

zero2ten_list = [x**2 for x in range(11)]
zero2ten_list
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

12.3.3사전 조건제시법

조건제시법을 이용하여 사전을 생성하는 과정도 유사하다. 아래 코드는 0부터 10 사이의 홀수를 키로, 홀수의 제곱을 값으로 갖는 항목으로 구성된 사전을 생성한다.

zero2ten_odd_dict = {x : x**2 for x in range(11) if x%2 == 1}
zero2ten_odd_dict
{1: 1, 3: 9, 5: 25, 7: 49, 9: 81}

아래 코드는 if 조건문을 사용하지 않아서 최종적으로 0부터 10 사이의 모든 정수와 정수들의 제곱의 쌍으로 구성된 사전을 생성한다.

zero2ten_dict = {x: x**2 for x in range(11)}
zero2ten_dict
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}

12.3.4튜플 조건제시법

조건제시법을 이용하여 튜플을 생성하는 과정은 tuple() 함수를 함께 사용한다는 점에서 조금 다르다. 엄밀히 말해 tuple() 함수의 인자로 사용되는 표현식은 튜플 조건제시법이 아니라 제너레이터 표현식이며, 여기서는 자세히 설명하지 않고, 편의상 튜플 조건제시법이라고 부른다.

예를 들어 아래 코드는 0부터 10 사이의 홀수들의 제곱으로 구성된 튜플을 생성한다.

zero2ten_odd_tuple = tuple(x**2 for x in range(11) if x%2 == 1)
zero2ten_odd_tuple
(1, 9, 25, 49, 81)

아래 코드는 if 조건문을 사용하지 않아서 최종적으로 0부터 10 사이의 모든 정수들의 제곱으로 구성된 튜플을 생성한다.

zero2ten_tuple = tuple(x**2 for x in range(11))
zero2ten_tuple
(0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100)

12.3.5예제

예제 1: 집합 조건제시법

아래 리스트에는 중복된 단어가 포함되어 있다.

words = ["python", "java", "python", "c", "java", "go", "rust"]

집합 조건제시법을 이용하여 단어들의 길이로 구성된 집합을 생성하라. 실행 결과는 다음과 같아야 한다.

{1, 2, 4, 6}

답:

집합 조건제시법은 중괄호 {} 안에 생성할 값과 for 반복문을 함께 작성한다. 여기서는 각 단어 word의 길이 len(word)를 집합의 항목으로 추가하면 된다. 집합은 중복 항목을 허용하지 않으므로 같은 길이는 한 번만 저장된다.

"python"의 길이는 6, "java""rust"의 길이는 4, "c"의 길이는 1, "go"의 길이는 2이다. 따라서 결과는 {1, 2, 4, 6}이다.

word_lengths = {len(word) for word in words}
word_lengths
{1, 2, 4, 6}

예제 2: 리스트 조건제시법

아래 리스트는 여러 학생의 점수로 구성되어 있다.

scores = [55, 70, 82, 43, 91, 66]

리스트 조건제시법을 이용하여 60점 이상인 점수만 모은 리스트를 생성하라. 실행 결과는 다음과 같아야 한다.

[70, 82, 91, 66]

답:

리스트 조건제시법은 대괄호 [] 안에 생성할 값, for 반복문, if 조건문을 함께 작성한다. 60점 이상인 점수만 모아야 하므로 if score >= 60 조건을 사용한다.

scores의 항목 중에서 70, 82, 91, 66만 60 이상이다. 따라서 결과는 [70, 82, 91, 66]이다.

passed_scores = [score for score in scores if score >= 60]
passed_scores
[70, 82, 91, 66]

예제 3: 사전 조건제시법

아래 리스트는 학생 이름으로 구성되어 있다.

names = ["김강현", "황현", "남세원", "최흥선"]

사전 조건제시법을 이용하여 학생 이름을 키로, 이름의 글자 수를 값으로 갖는 사전을 생성하라. 실행 결과는 다음과 같아야 한다.

{"김강현": 3, "황현": 2, "남세원": 3, "최흥선": 3}

답:

사전 조건제시법은 중괄호 {} 안에 키: 값 형식의 표현식과 for 반복문을 함께 작성한다. 학생 이름을 키로, 이름의 글자 수를 값으로 사용하면 된다.

"김강현", "남세원", "최흥선"은 세 글자이고, "황현"은 두 글자이다. 따라서 결과는 {"김강현": 3, "황현": 2, "남세원": 3, "최흥선": 3}이다.

name_lengths = {name: len(name) for name in names}
name_lengths
{'김강현': 3, '황현': 2, '남세원': 3, '최흥선': 3}

예제 4: 튜플 생성

아래 리스트는 섭씨 온도로 구성되어 있다.

celsius = [0, 10, 20, 30]

tuple() 함수를 이용하여 화씨 온도로 구성된 튜플을 생성하라. 화씨 온도는 다음 공식으로 계산한다.

fahrenheit = celsius * 9 / 5 + 32

실행 결과는 다음과 같아야 한다.

(32.0, 50.0, 68.0, 86.0)

답:

tuple() 함수에 제너레이터 표현식을 전달하면, 생성된 값들을 튜플로 묶을 수 있다. 여기서는 섭씨 온도 temp를 하나씩 꺼내 화씨 온도 temp * 9 / 5 + 32로 변환한다.

0, 10, 20, 30도는 각각 화씨 32.0, 50.0, 68.0, 86.0도이다. 따라서 결과는 (32.0, 50.0, 68.0, 86.0)이다.

fahrenheit_tuple = tuple(temp * 9 / 5 + 32 for temp in celsius)
fahrenheit_tuple
(32.0, 50.0, 68.0, 86.0)

섭씨를 화씨로 변환하는 함수를 이용할 수도 있다.

# 섭씨 => 화씨 변환 함수
def celsius_to_fahrenheit(celsius):
    return celsius * 9 / 5 + 32

fahrenheit_tuple = tuple(celsius_to_fahrenheit(temp) for temp in celsius)
fahrenheit_tuple
(32.0, 50.0, 68.0, 86.0)

12.3.6연습문제

문제 1: 동아리 출석 정리

파이썬 동아리에서 이번 주 모임에 참석한 학생 이름이 다음과 같이 기록되었다. 같은 학생이 여러 번 기록된 경우도 있다.

attendees = ["김강현", "황현", "김강현", "남세원", "최흥선", "황현", "남세원"]

집합 조건제시법을 이용하여 실제로 참석한 학생 이름으로 구성된 집합을 생성하라. 실행 결과는 다음과 같아야 한다.

{"김강현", "황현", "남세원", "최흥선"}

문제 2: 재시험 대상자 찾기

아래 리스트는 학생들의 시험 점수로 구성되어 있다.

scores = [82, 45, 67, 59, 91, 38, 76]

리스트 조건제시법을 이용하여 60점 미만인 점수만 모은 리스트를 생성하라. 실행 결과는 다음과 같아야 한다.

[45, 59, 38]

문제 3: 상품 할인표 만들기

문구점에서 다음 상품들의 원래 가격이 사전으로 주어졌다.

prices = {"연필": 500,
          "공책": 1200,
          "지우개": 800,
          "필통": 5000}

사전 조건제시법을 이용하여 모든 상품 가격을 10% 할인한 새 사전을 생성하라. 단, 할인 가격은 정수로 저장한다. 실행 결과는 다음과 같아야 한다.

{"연필": 450, "공책": 1080, "지우개": 720, "필통": 4500}

문제 4: 짧은 단어만 고르기

아래 리스트는 검색창에 입력된 단어들이다.

keywords = ["python", "ai", "data", "go", "code", "ml", "programming"]

튜플 조건제시법을 이용하여 길이가 3 이하인 단어만 모은 튜플을 생성하라. 실행 결과는 다음과 같아야 한다.

("ai", "go", "ml")