파이썬은 수천 개의 외부 라이브러리를 갖고 있지만, 그 중 가장 강력한 도구들이 이미 표준 라이브러리에 포함되어 있다는 사실을 알고 계셨나요?

이 시리즈는 '파이썬 표준 라이브러리 심화'를 주제로, 많이 쓰이지만 깊이 있게 다루어지지 않는 표준 라이브러리를 하나씩 파헤쳐 보려 합니다.

단순한 함수 나열이 아니라 실전 예제 중심으로 개념을 파악하고, 코드 응용력까지 키우는 것함으로써 여러분의 파이썬 활용력을 한 단계 높이는 것이 목표입니다.


collections 심화 사용법: 기본을 넘어서 실전 활용까지

1. 왜 collections부터 시작하는가

collections는 파이썬 내장 자료형(list, dict, tuple)을 보완하면서도, 성능과 구조 면에서 매우 효율적인 고급 컬렉션들을 제공합니다. 실무에서 자주 등장하지만, 깊이 있게 다뤄지는 경우는 많지 않습니다.

이 글에서는 그 중 가장 실용적인 다섯 가지 클래스를 중심으로, '왜 쓰는지', '어떻게 쓰는지', '언제 쓰면 좋은지'를 알려드리겠습니다.


2. Counter – 빈도 세기의 정석, 그 이상

Count all the things!

기본 개념

collections.Counter는 파이썬의 표준 라이브러리 collections 모듈에 포함된 매우 유용한 클래스 중 하나입니다. 말 그대로 "카운터"로, 데이터의 등장 횟수(빈도수)를 세기 위해 최적화된 특수한 딕셔너리라고 보면 됩니다.

from collections import Counter

c = Counter(['a', 'b', 'c', 'a', 'b', 'a'])
print(c)  # Counter({'a': 3, 'b': 2, 'c': 1})

리스트, 문자열, 튜플, 딕셔너리 등 이터러블 객체(iterable)를 넣으면 각 요소가 몇 번 나왔는지 세어주는 자료구조입니다.


주요 기능 및 메서드

📌 다양한 초기화 방식

Counter는 다양한 방식으로 초기화할 수 있어서 유연하게 데이터를 분석할 수 있습니다.

from collections import Counter

print(Counter(['a', 'b', 'a']))
# Counter({'a': 2, 'b': 1}) → 리스트

print(Counter({'a': 2, 'b': 1}))
# Counter({'a': 2, 'b': 1}) → 딕셔너리

print(Counter(a=2, b=1))
# Counter({'a': 2, 'b': 1}) → 키워드 인수

📌 요소 접근

Counterdict처럼 동작하지만, 존재하지 않는 키에 접근할 경우 KeyError 대신 0을 반환합니다.

c = Counter('hello')
print(c['l'])  # 2 (문자 'l'은 2번 등장)
print(c['x'])  # 0 ('x'는 등장하지 않았지만 에러 대신 0)

📌 요소 추가/수정

기존 요소에 값을 더하거나 직접 수정할 수 있습니다. 존재하지 않는 키도 자동으로 추가됩니다.

c = Counter('hello')
c['l'] += 3
print(c)
# Counter({'l': 5, 'o': 1, 'h': 1, 'e': 1})

📌 most_common(n) – 자주 등장하는 요소 추출

등장 빈도가 높은 순으로 n개 요소를 튜플 리스트로 반환합니다.

c = Counter('banana')
print(c.most_common(2))
# [('a', 3), ('n', 2)] → 'a'는 3번, 'n'은 2번 등장

📌 elements() – 요소를 반복하는 이터레이터

카운터 값을 기준으로 해당 요소를 반복하는 이터레이터를 제공합니다.

c = Counter('banana')
print(list(c.elements()))
# ['b', 'a', 'a', 'a', 'n', 'n']

단, 값이 0 이하인 요소는 포함되지 않습니다.


📌 수학 연산 지원 (Counter 간의 +, -, &, | 연산)

Counter는 딕셔너리보다 강력한 점 중 하나가 산술/집합 연산을 지원한다는 것입니다.

c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)

print(c1 + c2)
# Counter({'a': 4, 'b': 3}) → 같은 키는 합쳐짐

print(c1 - c2)
# Counter({'a': 2}) → 음수는 무시됨, 'b'는 음수가 되므로 생략

print(c1 & c2)
# Counter({'a': 1, 'b': 1}) → 교집합, 최소값 기준

print(c1 | c2)
# Counter({'a': 3, 'b': 2}) → 합집합, 최대값 기준

실전 활용 예시

📌 문자열 단어 분석
text = "the quick brown fox jumps over the lazy dog"
counter = Counter(text.split())
print(counter)
📌 로그 빈도 분석
logs = ['INFO', 'ERROR', 'INFO', 'DEBUG', 'ERROR', 'ERROR']
print(Counter(logs))  # Counter({'ERROR': 3, 'INFO': 2, 'DEBUG': 1})
📌 리스트의 중복 요소 카운팅
nums = [1, 2, 2, 3, 3, 3]
print(Counter(nums))  # Counter({3: 3, 2: 2, 1: 1})

주의할 점

  • Counterdict를 상속하지만 정렬을 보장하지 않습니다. 순서가 필요하다면 most_common()을 활용해야 합니다.
  • 값이 0 이하로 떨어져도 항목이 제거되지는 않으며, 직접 필터링해야 할 수 있습니다.
c = Counter(a=3)
c.subtract({'a': 5})
print(c)  # Counter({'a': -2})  # 값이 0 이하로 내려가도 항목은 사라지지 않음에 주의

팁: 초기화 없이 누적하는 방법

counter = Counter()
with open("data.txt") as f:
    for line in f:
        counter.update(line.strip().split())

정리

collections.Counter는 데이터 분석, 로그 처리, 텍스트 마이닝 등에서 거의 필수처럼 사용되는 강력한 도구입니다. 초보자에게는 쉬운 빈도수 세기 도구로, 숙련자에게는 연산과 필터링을 조합한 고급 처리 도구로 진화할 수 있습니다.


다음 편 예고

  • defaultdict – KeyError 없는 세상, dict보다 더 유연하게! 다음편도 기대해 주세요!

표준 라이브러리를 '깊이 있게 이해하고 제대로 쓰는 것'만으로도 여러분의 코드 품질은 분명 달라질 것입니다.