collections 모듈
1. collections 모듈
Python의 collections 모듈은 다양한 형태의 컨테이너를 제공하여 데이터 조작을 쉽고 효율적으로 할 수 있게 합니다. 특히 deque와 Counter는 알고리즘 문제 풀이에서도 많이 사용됩니다. 여기서는 자주 사용하는 것들을 차례로 살펴보고, 이전에는 유용했으나 이제는 잘 쓰지 않는 OrderedDict도 함께 다루겠습니다.
1.1 deque
deque는 양쪽 끝에서 요소를 추가하거나 제거할 수 있는 스레드-안전한 양방향 큐입니다. 리스트와 비슷하지만 양쪽 끝에서의 데이터 처리가 훨씬 효율적입니다.
from collections import deque
d = deque()
d.append('a') # 오른쪽 끝에 추가
d.appendleft('b') # 왼쪽 끝에 추가
d.pop() # 오른쪽 끝 요소 제거
d.popleft() # 왼쪽 끝 요소 제거
- Max Length:
deque는maxlen매개변수를 설정하여 고정된 크기를 가질 수 있습니다. 이 크기를 초과하여 요소를 추가하면, 반대쪽 끝의 요소가 자동으로 제거됩니다.from collections import deque d = deque(maxlen=3) d.extend([1, 2, 3]) # deque([1, 2, 3]) d.append(4) # deque([2, 3, 4]), 1이 밀려납니다. print(d) - 고성능:
deque는 양 끝에 요소를 추가하거나 제거할 때 리스트보다 훨씬 빠르며, 메모리 사용이 효율적입니다. - 스레드-안전(Thread-Safe):
deque는 멀티스레딩 환경에서 안전하게 사용할 수 있습니다. 여러 스레드에서 동시에deque에 접근하더라도 문제가 발생하지 않습니다. - 양방향 처리: 데이터를 양쪽 끝에서 추가하거나 제거할 수 있어, 큐나 스택과 같은 다양한 데이터 구조를 쉽게 구현할 수 있습니다.
1.2 Counter
Counter는 요소의 발생 횟수를 세는 데 사용되는 특수한 종류의 딕셔너리입니다. 이는 각 요소를 키로 하고 그 요소의 개수를 값으로 하는 컬렉션입니다.
from collections import Counter
c = Counter('hello world')
print(c) # 각 요소의 개수를 세어 딕셔너리로 반환
- 데이터 업데이트:
update()메서드를 사용하여 카운터에 새로운 데이터를 추가할 수 있습니다. 이는 기존의 카운트에 추가됩니다.c.update('hello') print(c) # 'h'와 'e', 'l', 'o'의 카운트가 증가됨 - 상위 N개 요소 얻기:
most_common()메서드를 사용하여 가장 흔한 요소들을 얻을 수 있습니다.print(c.most_common(3)) # 가장 흔한 세 요소 반환 - 수학적 연산:
Counter객체는 덧셈, 뺄셈 등의 수학적 연산도 지원합니다.c1 = Counter('hello') c2 = Counter('world') print(c1 + c2) # 두 카운터 합치기 print(c1 - c2) # c1에서 c2 제거
1.3 defaultdict
defaultdict는 없는 키에 접근했을 때 KeyError를 내는 대신 기본값을 자동으로 만들어주는 딕셔너리입니다. 생성할 때 기본값을 만들 함수를 넘겨줍니다.
from collections import defaultdict
# 값이 리스트인 딕셔너리
d = defaultdict(list)
d['fruits'].append('apple') # 키가 없어도 빈 리스트를 만들고 시작합니다.
d['fruits'].append('banana')
print(d)
일반 딕셔너리로 같은 일을 하려면 매번 키의 존재를 확인해야 합니다.
d = {}
for word in ['apple', 'avocado', 'banana', 'blueberry']:
key = word[0]
if key not in d: # 이 확인이 매번 필요합니다.
d[key] = []
d[key].append(word)
print(d)
from collections import defaultdict
d = defaultdict(list)
for word in ['apple', 'avocado', 'banana', 'blueberry']:
d[word[0]].append(word) # 확인 없이 바로 추가합니다.
print(dict(d))
개수를 셀 때는 defaultdict(int)를 사용합니다. int()는 0을 반환하기 때문입니다.
from collections import defaultdict
count = defaultdict(int)
for c in 'hello world':
count[c] += 1
print(dict(count))
1.4 namedtuple
namedtuple은 인덱스뿐만 아니라 이름으로도 값을 꺼낼 수 있는 튜플을 만들어줍니다. x[0], x[1]처럼 숫자로 접근하면 그 값이 무엇인지 알기 어려운데, 이를 해결해줍니다.
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
print(p.x, p.y) # 이름으로 접근
print(p[0]) # 튜플처럼 인덱스로도 접근
print(p) # Point(x=10, y=20)
튜플이므로 값을 바꿀 수 없습니다. 값을 바꿔야 한다면 _replace로 새 객체를 만듭니다.
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
p2 = p._replace(x=100) # 새 객체가 만들어집니다.
print(p, p2)
1.5 OrderedDict
OrderedDict는 키의 삽입 순서를 기억하는 딕셔너리입니다.
Python 3.7 버전부터는 딕셔너리가 삽입순서를 기억하게 되었습니다. 따라서 이제 이 모듈은 잘 사용되지 않습니다.
from collections import OrderedDict
od = OrderedDict()
od['a'] = 1
od['b'] = 2
print(od) # 출력: OrderedDict([('a', 1), ('b', 2)])
1.6 ChainMap
ChainMap은 여러 딕셔너리를 하나로 묶어서 사용할 수 있게 해주는 클래스입니다. 여러 딕셔너리를 하나로 묶어서 사용할 때, 첫 번째 딕셔너리부터 차례대로 키를 찾아나가는 방식으로 동작합니다. 앞에 있는 값에 우선순위를 주고 싶다면 이 자료형을 사용하세요.
from collections import ChainMap
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
cm = ChainMap(dict1, dict2)
print(cm['b']) # dict1의 'b' 키를 찾아서 2 출력