리스트에 같은 값이 여러 번 들어왔을 때 set()으로 감싸면 중복을 빠르게 없앨 수 있습니다. 하지만 list(set(items))만 외워서 사용하면 원래 순서가 달라져 당황할 수 있습니다.
이 글에서는 set으로 중복을 제거하는 기본 방법부터 순서를 유지해야 할 때의 dict.fromkeys(), 빈 집합을 만드는 법, add()와 remove()·discard()의 차이까지 직접 실행한 결과로 확인합니다.

확인한 환경
- 작성 및 실행 확인 : 2026년 8월 18일
- 운영체제 : Windows
- 직접 실행 : Python 3.9.13
- 공식 문서 확인 : Python 3.14.6 데이터 구조와 내장형 문서
본문의 예제 파일 6개를 직접 실행했습니다. 중복 제거 결과와 집합 연산을 확인했고, 존재하지 않는 값을 remove()로 삭제했을 때 KeyError가 발생하는 것도 재현했습니다.
set은 중복 없는 값의 모음입니다
장바구니에서 상품 이름이 중복된 상황을 만들어 보겠습니다.
items = ["사과", "바나나", "사과", "딸기", "바나나"]
unique_items = set(items)
print("중복 제거 개수 :", len(unique_items))
print("정렬해서 확인 :", sorted(unique_items))
실행 결과입니다.
중복 제거 개수 : 3
정렬해서 확인 : ['딸기', '바나나', '사과']
리스트에는 값이 5개 있었지만 set(items)에는 고유한 값 3개만 남습니다. 공식 Python 튜토리얼도 집합을 중복 원소가 없는 순서 없는 컬렉션으로 설명하며, 기본 용도로 중복 제거와 포함 여부 검사를 듭니다.
결과를 sorted()로 감싼 이유는 화면에서 일정한 순서로 확인하기 위해서입니다. set 자체에는 리스트의 첫 번째, 두 번째와 같은 위치 개념이 없습니다. 실행 환경에 따라 출력 순서가 달라질 수 있으므로 다음처럼 특정 순서를 전제로 작성하면 안 됩니다.
unique_items = set(items)
# unique_items[0] # TypeError: 'set' object is not subscriptable
인덱스로 값을 꺼내야 한다면 집합을 목적에 맞게 정렬한 리스트로 바꿉니다.
sorted_items = sorted(set(items))
print(sorted_items[0])
중복 제거 뒤 원래 순서가 필요하면 dict.fromkeys를 씁니다
입력 순서에서 처음 나온 값만 남기고 싶을 때 list(set(items))는 맞지 않습니다. Python 3.7 이상에서 딕셔너리는 삽입 순서를 보장하므로 dict.fromkeys()를 이용할 수 있습니다.
items = ["사과", "바나나", "사과", "딸기", "바나나"]
unique_in_order = list(dict.fromkeys(items))
print(unique_in_order)
['사과', '바나나', '딸기']
dict.fromkeys(items)는 리스트의 값을 딕셔너리 키로 사용합니다. 딕셔너리 키는 중복될 수 없고 처음 삽입된 위치가 유지되므로, 이를 다시 list()로 바꾸면 처음 등장한 순서대로 고유 값만 남습니다.
목적에 따라 다음처럼 고르면 됩니다.
| 필요한 결과 | 사용할 코드 |
|---|---|
| 순서는 상관없고 고유 값만 필요 | set(items) |
| 고유 값을 정렬해서 필요 | sorted(set(items)) |
| 처음 등장한 순서를 유지 | list(dict.fromkeys(items)) |
중복 제거만 필요하다고 무조건 리스트로 다시 바꿀 필요는 없습니다. 뒤에서 포함 여부를 검사하거나 다른 집합과 비교할 예정이라면 set 상태로 사용하는 편이 목적을 더 분명하게 보여줍니다.
빈 set은 중괄호가 아니라 set()으로 만듭니다
값이 들어 있는 집합은 중괄호로 만들 수 있습니다.
colors = {"red", "green", "blue"}
그러나 빈 중괄호 {}는 집합이 아니라 딕셔너리입니다.
empty_braces = {}
empty_set = set()
print(type(empty_braces))
print(type(empty_set))
<class 'dict'>
<class 'set'>
빈 집합은 반드시 set()으로 만듭니다. 나중에 값을 하나씩 추가할 계획이라면 다음처럼 시작하면 됩니다.
tags = set()
tags.add("python")
앞선 글에서 다룬 파이썬 딕셔너리 get 사용법과 KeyError 해결처럼 딕셔너리도 중괄호를 사용합니다. 차이는 {"name": "민수"}처럼 콜론으로 키와 값을 연결하는지, {"red", "green"}처럼 값만 나열하는지에 있습니다.
add로 추가하고 in으로 포함 여부를 확인합니다
집합에 값 하나를 넣을 때는 add()를 사용합니다. 리스트의 append()와 이름이 다릅니다.
colors = {"red", "green"}
colors.add("blue")
colors.add("red")
colors.discard("purple")
print(sorted(colors))
print("red" in colors)
print("purple" in colors)
['blue', 'green', 'red']
True
False
이미 들어 있는 red를 다시 추가해도 하나만 남습니다. in 연산자는 값이 집합에 있으면 True, 없으면 False를 돌려줍니다. 리스트에서도 같은 문법을 사용할 수 있으며, 자세한 조건식은 파이썬 in 연산자 사용법에서 이어서 확인할 수 있습니다.
remove와 discard는 없는 값을 지울 때 다릅니다
remove()와 discard()는 모두 집합에서 값을 삭제합니다. 삭제할 값이 실제로 들어 있을 때는 결과가 같습니다.
colors = {"red", "green", "blue"}
colors.remove("blue")
colors.discard("green")
차이는 없는 값을 지정할 때 드러납니다.
colors = {"red", "green", "blue"}
colors.remove("purple")
Python 3.9.13에서 실행한 오류입니다.
Traceback (most recent call last):
File "05_remove_keyerror.py", line 2, in <module>
colors.remove("purple")
KeyError: 'purple'

remove("purple")는 해당 값이 없으면 KeyError를 발생시킵니다. 반면 discard("purple")는 값이 없어도 오류 없이 넘어갑니다.
| 메서드 | 값이 있을 때 | 값이 없을 때 |
|---|---|---|
remove(value) |
삭제 | KeyError 발생 |
discard(value) |
삭제 | 아무 일도 하지 않음 |
반드시 있어야 하는 값이 사라진 상황을 오류로 발견하고 싶다면 remove()가 알맞습니다. 값이 있으면 지우고 없어도 계속 실행하려면 discard()가 간단합니다. if value in colors:로 먼저 확인한 뒤 remove()를 호출하는 방법도 있습니다.
합집합·교집합·차집합을 코드로 구합니다
집합은 여러 목록 사이의 공통 항목과 다른 항목을 찾을 때 특히 편합니다. 백엔드와 프런트엔드 수강자 명단을 비교해 보겠습니다.
backend = {"민수", "지수", "서준"}
frontend = {"지수", "서준", "하늘"}
print("둘 다 수강 :", sorted(backend & frontend))
print("한 과목 이상 :", sorted(backend | frontend))
print("백엔드만 수강 :", sorted(backend - frontend))
둘 다 수강 : ['서준', '지수']
한 과목 이상 : ['민수', '서준', '지수', '하늘']
백엔드만 수강 : ['민수']
backend & frontend: 두 집합에 모두 있는 교집합backend | frontend: 어느 한쪽에라도 있는 합집합backend - frontend: 왼쪽 집합에만 있는 차집합backend ^ frontend: 한쪽에만 있고 양쪽 공통은 아닌 대칭 차집합
메서드 이름으로는 각각 intersection(), union(), difference(), symmetric_difference()를 사용할 수 있습니다. 짧은 계산에는 연산자가 읽기 쉽고, 메서드 이름이 팀의 코드 규칙에 더 잘 맞으면 메서드를 선택해도 결과는 같습니다.
set에 리스트를 넣으면 TypeError가 납니다
집합의 원소는 해시 가능한 값이어야 합니다. 문자열, 숫자, 조건을 충족하는 튜플은 넣을 수 있지만 값이 바뀔 수 있는 리스트는 집합 원소로 넣을 수 없습니다.
values = {[1, 2], [3, 4]}
이 코드는 집합을 만드는 시점에 다음 오류를 발생시킵니다.
TypeError: unhashable type: 'list'
두 값의 묶음을 집합 원소로 사용하려면 리스트 대신 튜플로 바꿀 수 있습니다.
points = {(1, 2), (3, 4), (1, 2)}
print(sorted(points))
[(1, 2), (3, 4)]
튜플의 기본 성질과 리스트와의 선택 기준은 바로 앞 글인 파이썬 튜플과 리스트 차이에서 확인할 수 있습니다.
바로 확인할 핵심
set(items)는 리스트의 중복을 제거하지만 원래 순서를 보장하지 않습니다. 정렬된 결과가 필요하면 sorted(set(items)), 처음 등장한 순서를 유지해야 하면 list(dict.fromkeys(items))를 사용하세요.
빈 집합은 {}가 아니라 set()으로 만들고, 값을 추가할 때는 add()를 씁니다. 삭제할 값이 없어도 괜찮다면 discard(), 없다는 사실을 오류로 확인해야 한다면 remove()가 맞습니다. 이 차이까지 구분하면 중복 제거용 한 줄을 넘어 실제 데이터 비교에도 set을 안전하게 사용할 수 있습니다.
참고한 공식 문서
'IT프로그래밍 > 파이썬' 카테고리의 다른 글
| 파이썬 enumerate와 zip 사용법 : 번호와 두 리스트를 함께 꺼내기 (0) | 2026.08.26 |
|---|---|
| 파이썬 리스트 컴프리헨션 : for문 한 줄의 순서와 if 위치 (0) | 2026.08.21 |
| 파이썬 파일 읽기 쓰기 : with open으로 주문장을 남기는 방법 (0) | 2026.08.20 |
| 파이썬 try except 사용법 : ValueError와 KeyError 예외 처리 (0) | 2026.08.20 |
| 파이썬 튜플과 리스트 차이 : 원소 1개에는 왜 쉼표가 필요할까 (0) | 2026.08.14 |
| 파이썬 딕셔너리 사용법 : KeyError를 get으로 막는 가장 쉬운 방법 (0) | 2026.08.14 |
| 파이썬 in 연산자 기초 : 문자열 속 단어를 어떻게 찾을까 (0) | 2026.08.11 |
| 파이썬 문자열 메서드 기초 : strip과 replace는 원본을 바꿀까 (0) | 2026.08.10 |
댓글