집합(set)은 같은 값을 한 번만 담고 차례가 없는 묶음입니다. 리스트를 집합으로 바꾸면 겹친 값이 하나로 합쳐지고, 두 집합에 함께 있는 값이나 한쪽에만 있는 값을 기호 하나로 구합니다.
오늘 주문 리스트에는 같은 메뉴가 여러 번 들어 있습니다. 팔린 메뉴가 몇 가지인지, 어제와 오늘 모두 팔린 메뉴는 무엇인지 리스트로 구하려면 값을 하나하나 견줘야 합니다. 집합은 겹친 값을 저절로 하나로 합치고, 두 집합을 견주는 계산을 연산자 하나로 합니다.
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
Truein은 리스트와 딕셔너리에도 씁니다. 딕셔너리에 쓰면 무엇이 찍힐까요?
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를 받습니다.컴퓨터에서 해 보기
파이썬 3.14. Tab을 누르면 다음 단추나 칸으로 갑니다. 들여쓰기는 빈칸 네 개입니다. Enter는 앞줄 들여쓰기를 잇고, 콜론 뒤에서는 한 단계 더 들여 씁니다. Ctrl+Enter로 실행합니다.
'실행'을 누르면 여기에 나옵니다.
여기서 쓴 코드 · 내 예상은 저장하지도 운영자에게 보내지도 않습니다. 페이지를 새로 열면 처음 코드입니다.