본문으로 건너뛰기

집합: 겹치지 않게 모으기

파이썬 목차딕셔너리와 집합레슨 35 / 59

목차

집합(set)은 같은 값을 한 번만 담고 차례가 없는 묶음입니다. 리스트를 집합으로 바꾸면 겹친 값이 하나로 합쳐지고, 두 집합에 함께 있는 값이나 한쪽에만 있는 값을 기호 하나로 구합니다.

오늘 주문 리스트에는 같은 메뉴가 여러 번 들어 있습니다. 팔린 메뉴가 몇 가지인지, 어제와 오늘 모두 팔린 메뉴는 무엇인지 리스트로 구하려면 값을 하나하나 견줘야 합니다. 집합은 겹친 값을 저절로 하나로 합치고, 두 집합을 견주는 계산을 연산자 하나로 합니다.

6단계 중 1단계. 다음을 누르면 1번 줄부터 실행합니다.

1겹친 값은 하나만, 차례는 없다

set()에 리스트를 넘기면 그 값들로 집합을 만듭니다.

orders = ['커피', '라테', '커피']
kinds = set(orders)
print(len(orders))
print(len(kinds))
print(sorted(kinds))
실행 결과
3
2
['라테', '커피']

집합은 같은 값을 한 번만 담아, 두 번 든 '커피'가 하나로 합쳐졌습니다. 리스트에는 값이 셋, 집합에는 둘입니다. 집합에는 차례가 없습니다. 자리 번호로 꺼낼 수 없고, 집합을 그대로 찍으면 값이 찍히는 차례가 실행할 때마다 다를 수 있습니다. 그래서 이 과목은 집합을 sorted()로 정렬한 리스트로 바꿔 찍습니다. 집합을 바로 만들 때는 {'커피', '라테'}처럼 중괄호 안에 값만 씁니다. 딕셔너리와 달리 콜론이 없습니다.

2in으로 들어 있는지 보기

in은 값이 들어 있는지 보고 True나 False를 냅니다. for x in …의 in이 꺼낼 값들을 가리키는 것과는 다른 일입니다.

kinds = {'커피', '라테'}
print('녹차' in kinds)
print('커피' in kinds)
실행 결과
False
True

in은 리스트와 딕셔너리에도 씁니다. 딕셔너리에 쓰면 무엇이 찍힐까요?

menu = {'커피': 4000}
print('커피' in menu)
print(4000 in menu)
실행 결과 보기
실행 결과
True
False

예상과 다르면 그곳이 배울 곳입니다. 딕셔너리에 쓴 in은 키만 봅니다. 4000은 키 '커피'의 값일 뿐 키가 아니라서 False입니다. 딕셔너리를 돌면 키가 나오는 것과 같습니다.

3합집합·교집합·차집합

두 집합을 견주는 연산자 셋입니다. 그림과 같은 집합입니다.

mon = {'커피', '라테'}
tue = {'라테', '녹차'}
print(sorted(mon | tue))
print(sorted(mon & tue))
print(sorted(mon - tue))
실행 결과
['녹차', '라테', '커피']
['라테']
['커피']
  • |(합집합, union): 어느 한쪽에라도 있는 값을 모두 모읍니다. 두 집합에 다 있는 '라테'도 한 번만 듭니다.
  • &(교집합, intersection): 두 집합에 다 있는 값만 모읍니다.
  • -(차집합, difference): 왼쪽 집합에만 있는 값을 남깁니다.

세 연산자 모두 새 집합을 만들 뿐, mon과 tue는 그대로입니다.

예제

팔리지 않은 메뉴 찾기

메뉴판 딕셔너리를 set()에 넘기면 키만 담은 집합이 됩니다. 메뉴 집합에서 팔린 메뉴 집합을 빼면 팔리지 않은 메뉴가 남습니다.

menu = {
    '커피': 4000,
    '라테': 4500,
    '쿠키': 2000,
}
orders = ['커피', '라테', '커피']
unsold = set(menu) - set(orders)
print(sorted(unsold))
실행 결과
['쿠키']

주문에 '커피'가 두 번 들었어도 set(orders)에는 한 번만 담깁니다. 차집합은 메뉴 쪽에만 있는 '쿠키'를 남깁니다.

자주 틀리는 곳

틀린 생각: 집합도 리스트처럼 차례가 있어 0번 자리로 꺼낼 수 있다.

값을 차례로 적어 만드니 적은 차례가 남을 것 같습니다. 집합은 값의 자리와 담은 차례를 기록하지 않습니다.

kinds = {'커피', '라테'}
print(kinds[0])
실행 결과
Traceback (most recent call last):  File "main.py", line 2, in <module>    print(kinds[0])          ~~~~~^^^TypeError: 'set' object is not subscriptable

맨 아래 줄은 집합(set)은 대괄호로 꺼낼 수 없다(not subscriptable)는 뜻입니다.

맞는 규칙: 집합은 같은 값을 한 번만 담을 뿐 차례를 기록하지 않아 자리 번호로 꺼낼 수 없습니다. 차례가 필요하면 sorted()로 정렬한 리스트로 바꿉니다.

틀린 생각: 빈 중괄호 {}는 빈 집합이다.

집합을 중괄호로 쓰니 빈 집합도 {}일 것 같습니다. 중괄호만 쓰면 딕셔너리가 먼저입니다.

print(type({}))
print(type(set()))
실행 결과
<class 'dict'>
<class 'set'>

{}의 종류는 dict입니다. 빈 집합은 set()으로 만들어야 set이 됩니다.

맞는 규칙: 중괄호만 쓴 {}는 빈 딕셔너리입니다. 빈 집합은 set()으로 만듭니다.

확인 문제

1. 3절 코드의 2번 줄을 tue = {'라테', '커피'}로 바꿨습니다. 무엇이 찍히는지 세 줄을 글자 그대로 쓰세요.

mon = {'커피', '라테'}
tue = {'라테', '커피'}
print(sorted(mon | tue))
print(sorted(mon & tue))
print(sorted(mon - tue))
힌트

두 집합에 든 값을 견줘 봅니다. 차례는 따지지 않습니다.

정답
실행 결과
['라테', '커피']
['라테', '커피']
[]

두 집합은 적은 차례만 다를 뿐 같은 값을 담았습니다. 합집합과 교집합은 둘 다 '라테'와 '커피'이고, mon에만 있는 값이 없어 차집합은 빈 리스트 []로 찍힙니다.

흔한 오답은 첫 줄을 ['라테', '라테', '커피', '커피']⁠로 쓰는 것입니다. 합집합에 두 집합의 값이 겹친 채로 든다고 본 것입니다. 집합은 같은 값을 한 번만 담습니다.

2. 월요일과 화요일에 온 손님 이름을 집합에 담았습니다. 이틀 모두 온 손님 수 2가 찍히도록 빈칸에 연산자를 채우세요. 2번 줄이 tue = {'민지'}라면 1이 찍혀야 합니다.

mon = {'민지', '준호', '서연'}
tue = {'준호', '서연', '하준'}
both = mon ____ tue
print(len(both))
힌트

'두 집합에 다 있는 값'을 모으는 연산자를 3절에서 찾습니다.

정답

&입니다.

mon = {'민지', '준호', '서연'}
tue = {'준호', '서연', '하준'}
both = mon & tue
print(len(both))
실행 결과
2

이틀 모두 온 준호와 서연만 모여 2입니다.

다르게 써도 되는 곳: tue & mon도 같습니다. 교집합은 차례를 바꿔도 결과가 같습니다.

흔한 오답은 |입니다. 그러면 하루라도 온 손님 넷이 모여 4가 찍힙니다. |가 두 집합에 다 있는 값만 모은다고 본 것입니다.

3. 장바구니에 담은 과일을 집합으로 바꿨습니다. 무엇이 찍히는지 세 줄을 글자 그대로 쓰세요.

cart = ['사과', '배', '사과', '귤']
kinds = set(cart)
print(len(cart), len(kinds))
print('배' in kinds)
print(sorted(kinds))
힌트

리스트 cart에는 값이 몇 개 들었는지, 집합 kinds에는 몇 개가 남는지 따로 셉니다.

정답
실행 결과
4 3
True
['귤', '배', '사과']

리스트에는 값이 넷이지만, 집합은 겹친 '사과'를 하나로 합쳐 셋입니다. sorted()는 가나다 차례로 정렬한 리스트를 만듭니다.

흔한 오답은 첫 줄을 4 4로 쓰는 것입니다. 집합에도 겹친 값이 그대로 남는다고 본 것입니다.

5번 줄의 mon - tue를 tue - mon으로 바꾸면 찍히는 줄을 모두 봅니다.

처음 펼 때 약 6MB를 받습니다.컴퓨터에서 해 보기

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

파이썬 목차로