[파이썬 100강] 07강. 딕셔너리와 집합으로 데이터 다루기
딕셔너리(dict)와 집합(set)은 파이썬에서 데이터를 “빠르고 정확하게” 다루기 위해 반드시 익혀야 하는 핵심 자료구조입니다. 리스트가 순서 중심의 저장소라면, 딕셔너리는 키 기반 조회, 집합은 중복 제거와 집합 연산에 특화되어 있습니다. 실무에서는 로그 집계, API 응답 파싱, 사용자 권한 처리, 태그 중복 제거 같은 작업에서 거의 매일 사용합니다.
핵심 개념
딕셔너리는 key: value 쌍으로 데이터를 저장합니다. 핵심은 “인덱스 번호”가 아니라 “의미 있는 키”로 접근한다는 점입니다.
- 딕셔너리:
{"name": "건우", "level": 3} - 집합:
{"python", "data", "automation"}
집합은 순서가 없고 중복을 허용하지 않습니다. 그래서 “겹치는 값 찾기”, “중복 제거”, “포함 관계 확인”에서 압도적으로 편합니다.
>>> user = {"id": 101, "name": "kim", "active": True}
>>> user["name"]
'kim'
>>> user["role"] = "admin"
>>> user
{'id': 101, 'name': 'kim', 'active': True, 'role': 'admin'}
>>> tags = {"python", "api", "python", "devlab"}
>>> tags
{'devlab', 'python', 'api'}
딕셔너리와 집합의 공통점은 해시 기반이라 평균적으로 조회/검사가 빠르다는 점입니다. 따라서 데이터 크기가 커질수록 리스트 대비 이점이 커집니다.
기본 사용
1) 딕셔너리 생성, 조회, 안전 접근
>>> product = {"sku": "A-100", "name": "무선 키보드", "price": 49000}
>>> product["name"]
'무선 키보드'
>>> product.get("stock", 0)
0
>>> product["price"] = 45000
>>> product
{'sku': 'A-100', 'name': '무선 키보드', 'price': 45000}
dict.get()을 쓰면 키가 없을 때도 예외 없이 기본값을 받을 수 있어 실무 코드가 안정적입니다.
2) 반복문으로 딕셔너리 순회
>>> stats = {"views": 1520, "likes": 121, "comments": 18}
>>> for k, v in stats.items():
... print(f"{k}: {v}")
...
views: 1520
likes: 121
comments: 18
.items()는 키와 값을 동시에 다룰 때 가장 자주 사용합니다.
3) 집합으로 중복 제거와 포함 검사
>>> emails = ["[email protected]", "[email protected]", "[email protected]", "[email protected]", "[email protected]"]
>>> unique_emails = set(emails)
>>> unique_emails
{'[email protected]', '[email protected]', '[email protected]'}
>>> "[email protected]" in unique_emails
True
로그/이벤트 데이터에서 중복 이벤트를 빠르게 제거할 때 유용합니다.
4) 집합 연산으로 공통/차집합 처리
>>> team_a = {"kim", "lee", "park", "choi"}
>>> team_b = {"choi", "jung", "kim"}
>>> team_a & team_b # 교집합
{'kim', 'choi'}
>>> team_a - team_b # 차집합
{'lee', 'park'}
>>> team_a | team_b # 합집합
{'kim', 'lee', 'park', 'choi', 'jung'}
권한 그룹 비교, 기능 플래그 비교, 태그 분석 같은 작업을 짧고 명확하게 표현할 수 있습니다.
5) 실무형 예제: API 응답 집계
>>> responses = [
... {"endpoint": "/login", "status": 200},
... {"endpoint": "/login", "status": 401},
... {"endpoint": "/users", "status": 200},
... {"endpoint": "/users", "status": 200},
... ]
>>> status_count = {}
>>> for r in responses:
... code = r["status"]
... status_count[code] = status_count.get(code, 0) + 1
...
>>> status_count
{200: 3, 401: 1}
딕셔너리를 카운터처럼 활용하면 집계 로직을 간결하게 유지할 수 있습니다.
자주 하는 실수
실수 1) 없는 키를 바로 인덱싱해서 KeyError
>>> profile = {"name": "min"}
>>> profile["age"]
Traceback (most recent call last):
...
KeyError: 'age'
외부 입력(API/파일/사용자 입력)에서는 키가 없을 수 있으므로 get()이나 in 검사 후 접근하는 습관이 안전합니다.
실수 2) 집합이 순서를 보장한다고 착각
집합은 순서가 없습니다. 따라서 “첫 번째 요소” 같은 개념에 의존하면 환경에 따라 결과가 달라질 수 있습니다.
실수 3) 변경 가능한 객체를 딕셔너리 키로 사용
리스트 같은 mutable 객체는 해시 불가능이라 키가 될 수 없습니다.
>>> d = {}
>>> d[[1, 2, 3]] = "x"
Traceback (most recent call last):
...
TypeError: unhashable type: 'list'
키로는 문자열, 숫자, 튜플(내부도 해시 가능해야 함)처럼 불변 타입을 사용하세요.
실수 4) 중복 제거 후 원래 순서까지 보존된다고 생각
list(set(values))는 빠르지만 순서가 깨질 수 있습니다. 순서 보존이 필요하면 dict.fromkeys(values) 패턴을 고려하세요.
오늘의 결론
딕셔너리와 집합은 “목적에 맞는 데이터 구조 선택”의 출발점입니다.
- 의미 있는 키로 데이터를 관리할 때는 딕셔너리를 쓴다.
- 중복 제거, 포함 검사, 교집합/차집합은 집합이 가장 빠르고 명확하다.
- 외부 데이터는 키 누락을 전제로
get()기반 방어 코드를 작성한다. - 순서가 중요한지 여부를 먼저 판단하고
list/dict/set을 선택한다.
이 기준이 잡히면 다음 강의의 함수 설계에서도 매개변수/반환값 구조를 훨씬 깔끔하게 만들 수 있습니다.
연습문제
- 아래 리스트에서 중복을 제거하고, 고유 사용자 수를 출력해보세요.
users = ["kim", "lee", "kim", "park", "lee", "choi"]
sales = {"mon": 120, "tue": 95, "wed": 132}딕셔너리에서 요일과 매출을 순회하며월:120형태로 출력해보세요.- 두 집합
a = {1,2,3,4,5},b = {4,5,6,7}에 대해 교집합, 차집합(a-b), 합집합을 각각 출력해보세요.
이전 강의 정답 (06강 연습문제)
- 80점 이상 점수만 추출
>>> scores = [72, 88, 95, 64, 83]
>>> high_scores = [s for s in scores if s >= 80]
>>> high_scores
[88, 95, 83]
- 튜플 언패킹 + 요약 출력
>>> server = ("db-01", "192.168.0.10", 5432)
>>> name, ip, port = server
>>> print(f"서버 {name}는 {ip}:{port}에서 실행 중")
서버 db-01는 192.168.0.10:5432에서 실행 중
- 리스트를 독립 복사 후 원본 보호
>>> items = ["A", "B", "C"]
>>> items2 = items.copy()
>>> items2.append("D")
>>> items
['A', 'B', 'C']
>>> items2
['A', 'B', 'C', 'D']
실습 환경/재현 정보
- 실행 환경:
condaenvpython100(Python 3.11.14)