파이썬에는 리스트(list), 튜플(tuple), 셋(set), 딕셔너리(dictionary)라는 네 가지 데이터 컬렉션 타입이 내장되어 있습니다. 그리고 파이썬의 collections 모듈은 여기에 더해 namedtuple, Counter, defaultdict, ChainMap 같은 강력한 추가 옵션을 제공합니다.
컬렉션 기본 개념 복습
컬렉션(collection)은 데이터를 저장할 수 있는 컨테이너 자료형을 의미합니다. 각 자료형은 저마다 고유한 특징을 지니고 있어, 상황에 맞게 선택하는 것이 중요합니다.
예를 들어 계속 수정해야 하는 학생 명단을 저장한다면 변경 가능한 리스트가 적합합니다. 반면 절대 바뀌지 않을 아이스크림 맛 목록이라면 내용을 수정할 수 없는 튜플이 더 나은 선택일 수 있습니다.
하지만 실무에서 파이썬을 다루다 보면 기본 자료형만으로는 원하는 기능을 모두 구현하기 어려운 경우가 많습니다. 이럴 때 유용하게 쓸 수 있는 것이 바로 파이썬 collections 모듈입니다. 이 모듈은 내장 컬렉션의 기능을 확장하고, 데이터 구조를 다룰 때 더 큰 유연성을 제공하도록 설계되었습니다. 이 글에서는 collections 모듈의 기본 개념과 가장 많이 사용되는 네 가지 자료 구조를 살펴보겠습니다.
리스트(List)
리스트는 순서가 있고 변경 가능(mutable)한 자료형으로, 시간이 지나며 달라질 수 있는 데이터를 저장하는 데 적합합니다. 항목을 자유롭게 추가·삭제·수정할 수 있고, 중복 값도 허용됩니다. 인덱스 번호를 사용해 개별 항목에 접근할 수 있습니다.
sandwiches = ["Cheese", "Ham", "Tuna", "Egg Mayo"]
튜플(Tuple)
튜플은 순서가 있고 불변(immutable)인 자료형입니다. 중복 값은 포함할 수 있지만, 한번 생성된 값은 변경할 수 없습니다. 튜플은 소괄호 ()로 감싸서 선언합니다.
sandwiches = ("Cheese", "Ham", "Tuna", "Egg Mayo")셋(Set)
셋은 순서가 없는 자료형으로, 중괄호 {}를 사용해 선언합니다. 리스트와 달리 인덱스가 없으며, 중복된 요소를 허용하지 않습니다.
sandwiches = {"Cheese", "Ham", "Tuna", "Egg Mayo"}딕셔너리(Dictionary)
딕셔너리는 변경 가능하고 인덱싱이 가능한 자료형입니다. 각 항목은 키(key)와 값(value)의 쌍으로 구성됩니다.
sandwich = {
"name": "Cheese",
"price": 8.95
}이 네 가지 자료형은 파이썬에서 폭넓게 활용됩니다. 하지만 더 고급 작업이 필요하다면 collections 모듈을 고려해 볼 만합니다.
collections 모듈이란?
파이썬 collections 모듈은 내장 컨테이너를 대체하거나 보완할 수 있는 특수한 자료 구조들을 제공합니다. collections는 모듈이기 때문에 프로그램에 import 해야 하지만, 파이썬에 기본으로 포함되어 있어 별도의 외부 라이브러리 설치는 필요하지 않습니다.
이 글에서는 collections 모듈에서 가장 많이 쓰이는 네 가지 자료 구조를 집중적으로 다룹니다.
- Counter
- namedtuple
- defaultdict
- ChainMap
1. Counter — 요소 개수 세기
Counter()는 딕셔너리 객체의 서브클래스로, 해시 가능한(hashable) 객체의 개수를 셀 때 사용합니다. 반복 가능한(iterable) 객체를 인자로 받아 딕셔너리 형태의 결과를 반환합니다.
예를 들어 1월 샌드위치 주문 목록이 있고, 그달에 BLT 샌드위치가 몇 개 팔렸는지 알고 싶다고 가정해 봅시다. 이때 Counter() 함수를 활용할 수 있습니다.
from collections import Counter sandwich_sales = ["BLT", "Egg Mayo", "Ham", "Ham", "Ham", "Cheese", "BLT", "Cheese"] our_counter = Counter(sandwich_sales) print(our_counter["BLT"])
프로그램 실행 결과: 2
코드를 하나씩 살펴보겠습니다. 첫 번째 줄에서 collections 모듈로부터 Counter 함수를 가져옵니다. 다음 줄에서는 1월 판매량을 저장한 sandwich_sales 배열을 선언합니다. 그다음 our_counter 변수에 Counter(sandwich_sales)의 결과를 할당하여, 이후 언제든 결과에 접근할 수 있도록 합니다. 마지막으로 print(our_counter["BLT"])를 통해 딕셔너리에서 BLT에 해당하는 개수를 출력합니다. 결과는 2입니다.
2. namedtuple — 이름으로 값에 접근하기
namedtuple() 메서드는 튜플의 각 위치에 이름을 붙여준 튜플을 반환합니다. 일반 튜플에서는 인덱스 번호로만 개별 값에 접근할 수 있는데, 튜플이 커지면 이 방식은 금방 혼란스러워집니다.
다음은 namedtuple()을 사용해 샌드위치의 이름과 가격을 저장하는 예제입니다.
from collections import namedtuple
Sandwich = namedtuple("Sandwich", "name, price")
first_sandwich = Sandwich("Chicken Teriyaki", "$3.00")
print(first_sandwich.price)프로그램 실행 결과: $3.00
첫 줄에서 collections 모듈에서 namedtuple을 가져옵니다. 다음 줄에서는 Sandwich라는 이름의 튜플 클래스를 만들고 name과 price라는 두 개의 필드명을 지정합니다. 이렇게 하면 나중에 필드명으로 튜플의 값을 참조할 수 있습니다. 이후 first_sandwich 변수에 'Chicken Teriyaki' 샌드위치 항목을 할당하고, 마지막으로 가격을 출력합니다.
리스트를 사용해서도 namedtuple()을 만들 수 있습니다.
second_sandwich = Sandwich._make(["Spicy Italian", "$3.75"]) print(second_sandwich.name)
프로그램 실행 결과: Spicy Italian
이 예제에서는 Sandwich 객체에 _make를 함께 사용해 리스트를 namedtuple()로 변환했습니다.
3. defaultdict — KeyError 없이 안전하게
defaultdict() 메서드는 존재하지 않는 키에 접근할 때 KeyError를 발생시키지 않는 딕셔너리를 만들 수 있게 해줍니다. 대신 미리 정의해 둔 기본 자료형의 값을 반환합니다.
다음은 존재하지 않는 키에 접근하면 문자열(str)을 반환하도록 설정한 예제입니다.
from collections import defaultdict sandwiches = defaultdict(str) sandwiches[0] = "Ham and Cheese" sandwiches[1] = "BLT" print(sandwiches[1]) print(sandwiches[2])
프로그램 실행 결과:
BLT (빈 줄 출력)
위 예제에서는 인덱스 0과 1에 값이 있는 딕셔너리를 만들었습니다. sandwiches[1]을 출력하면 저장된 값이 정상적으로 나오지만, 값이 없는 인덱스 2를 출력하면 빈 줄이 반환됩니다.
일반 딕셔너리라면 여기서 KeyError가 발생했겠지만, defaultdict를 사용했기 때문에 생성 시 지정한 자료형의 기본값이 반환됩니다. 위 예제에서는 문자열을 지정했지만, 정수나 다른 유효한 자료형을 지정할 수도 있습니다.
이 기능은 여러 항목에 연산을 수행하되 일부 항목에서는 연산이 실패할 수 있는 경우 특히 유용합니다. 프로그램이 에러로 중단되는 대신 기본값을 반환하며 계속 실행됩니다.
4. ChainMap — 여러 딕셔너리 하나로 묶기
ChainMap() 메서드는 두 개 이상의 딕셔너리를 하나로 결합할 때 사용합니다. 예를 들어 기본 메뉴와 시크릿 메뉴 두 개의 딕셔너리를 하나의 큰 메뉴로 합치고 싶다면 ChainMap() 함수를 쓰면 됩니다.
from collections import ChainMap
standard_menu = { "BLT": "$3.05", "Roast Beef": "$3.55", "Cheese": "$2.85", "Shrimp": "$3.55", "Ham": "$2.85" }
secret_menu = { "Steak": "$3.60", "Tuna Special": "$3.20", "Turkey Club": "$3.20" }
menu = ChainMap(standard_menu, secret_menu)
print(menu)실행 결과, 두 메뉴가 합쳐진 ChainMap 객체가 반환됩니다.
ChainMap({'BLT': '$3.05', 'Roast Beef': '$3.55', 'Cheese': '$2.85', 'Shrimp': '$3.55', 'Ham': '$2.85'}, {'Steak': '$3.60', 'Tuna Special': '$3.20', 'Turkey Club': '$3.20'})키 이름을 참조하면 ChainMap의 각 값에 접근할 수 있습니다.
print(menu["BLT"])
프로그램 실행 결과: $3.05
또한 중요한 점은, ChainMap은 원본 딕셔너리가 변경되면 함께 업데이트된다는 것입니다. standard_menu나 secret_menu의 값을 바꾸면 ChainMap 객체에도 그대로 반영됩니다.
print(menu) standard_menu["BLT"] = "$3.10" print(menu)
실행 결과:
ChainMap({'BLT': '$3.10', 'Roast Beef': '$3.55', 'Cheese': '$2.85', 'Shrimp': '$3.55', 'Ham': '$2.85'}, {'Steak': '$3.60', 'Tuna Special': '$3.20', 'Turkey Club': '$3.20'})standard_menu에서 BLT 가격을 변경했더니 ChainMap 안의 BLT 가격도 $3.05에서 $3.10으로 바뀐 것을 확인할 수 있습니다.
ChainMap 객체에는 키나 값을 조회하는 함수도 포함되어 있습니다. keys()와 values() 메서드를 사용하면 됩니다.
print(list(menu.keys())) print(list(menu.values()))
실행 결과:
['Steak', 'Tuna Special', 'Turkey Club', 'BLT', 'Roast Beef', 'Cheese', 'Prawn', 'Ham'] ['$3.60', '$3.20', '$3.20', '$3.05', '$3.55', '$2.85', '$3.55', '$2.85']
그리고 new_child() 메서드를 사용하면 기존 ChainMap에 새로운 딕셔너리를 추가할 수 있습니다. 예를 들어 테스트 메뉴에서 검증된 신메뉴 두 개를 메뉴에 추가한다고 가정해 보겠습니다.
test_menu = { "Veggie Deluxe": "$3.00", "House Club Special": "$3.65" }
new_menu = menu.new_child(test_menu)
print(new_menu)실행 결과, 새 샌드위치들이 맨 앞에 추가된 업데이트된 ChainMap이 반환됩니다.
ChainMap({'Veggie Deluxe': '$3.00', 'House Club Special': '$3.65'}, {'BLT': '$3.05', 'Roast Beef': '$3.55', 'Cheese': '$2.85', 'Shrimp': '$3.55', 'Ham': '$2.85'}, {'Steak': '$3.60', 'Tuna Special': '$3.20', 'Turkey Club': '$3.20'})마무리
파이썬 collections 모듈을 활용하면 내장 컬렉션의 기능을 확장하고 커스텀 자료 구조 메서드를 사용할 수 있습니다. 리스트나 튜플 같은 컬렉션 자료형을 다루면서 기본 파이썬(vanilla Python)에서는 제공하지 않는 특정 기능이 필요할 때 특히 유용합니다.
이 가이드에서는 예제와 함께 파이썬 컬렉션의 기본 개념을 정리하고, collections 모듈의 핵심 메서드인 Counter, namedtuple, defaultdict, ChainMap의 사용법을 살펴보았습니다.
이제 여러분도 전문가처럼 파이썬 collections 모듈을 활용할 준비가 되었습니다!