[Python] collections 모듈의 컨테이너

김지환·2022년 10월 2일
0
post-custom-banner

Python 을 사용할 때면 대부분 list, dict, set 정도의 기본 컨테이너만 사용을 했었다. 하지만 이외에도 꽤나 유용하게 사용할 수 있는 컨테이너들이 있는데

Python에서 기본으로 제공해주는 collections 모듈을 이용하면 된다.

Collections

collections 모듈에는 다양한 컨테이너들이 있는데 그 중 유용하게 사용해볼 수 있는 컨테이너 몇 가지가 있다.

defaultdict

python 의 기본 dict를 사용할 때 다음과 같은 코드를 사용한 적이 있다.

...
b: dict 
a: dict ( list를 value 로 갖는 dict )
for key, value in b.items():
	a[key].append(value)

이렇게 될 때 만약 a 에 key 값이 존재 하지 않게 되면 분기처리를 하여서
a 에 해당 key 값으로 list값을 주거나 아래와 같이 setdefault 함수를 사용해줘야한다.

...
d = {}
for k, v in s:
    d.setdefault(k, []).append(v)

하지만 분기처리를 하면 코드의 가독성이 떨어지고, setdefault는 미리 key 값을 정의할 수 있는 경우에만 사용이 가능하다.
이 때 defaultdict를 사용하면 아주 유용하다.

import collections

int_dict = collections.defaultdict(int)
list_dict = collections.defaultdict(list)
dict_dict = collections.defaultdict(dict)
tuple_dict = collections.defaultdict(tuple)
set_dict = collections.defaultdict(set)
float_dict = collections.defaultdict(float)

print(int_dict["a"], list_dict["a"], dict_dict["a"], tuple_dict["a"], set_dict["a"],float_dict["a"])
print(f"{int_dict}\n{list_dict}\n{dict_dict}\n{tuple_dict}\n{set_dict}\n{float_dict}\n")

0 [] {} () set() 0.0
defaultdict(<class 'int'>, {'a': 0})
defaultdict(<class 'list'>, {'a': []})
defaultdict(<class 'dict'>, {'a': {}})
defaultdict(<class 'tuple'>, {'a': ()})
defaultdict(<class 'set'>, {'a': set()})
defaultdict(<class 'float'>, {'a': 0.0})

위와 같이 defaultdict 를 처음 선언하게 될 때 type 값을 넣어주게 되면 참조되는 key 값이 비어있으면 기본으로 각 type별 설정되어있는 default 값을 넣어서 생성해주게 된다.

당연히 아무 값을 할당해주지 않았을 때만 default 값을 가지게 되고 특정 값을 할당하면 그대로 dict 처럼 사용할 수 있다.

이외에도 custom 한 default 값을 사용하고 싶다면 아래와 같이 함수를 만들어서 사용할 수 있다.

def custom_factory():
    return ["b"]

custom_dict = collections.defaultdict(custom_factory)

print(custom_dict["a"], custom_dict["b"])

['b'] ['b']

상황에 맞게 defaultdict를 활용하면 더 간결하고 효율적으로 코드를 작성할 수 있을 것이다.

namedtuple

namedtuple은 튜플의 각 위치에 의미를 부여하고 더 읽기 쉽고 자체 문서화된 코드를 허용한다. 일반 튜플을 사용하는 모든 곳에서 사용할 수 있으며 위치 색인 대신 이름으로 필드에 액세스할 수 있는 기능을 추가할 수 있다.

named_tuple = collections.namedtuple("Point", ["x", "y", "z"])
p = named_tuple(1,2,3)

print(p[0], p[1], p[2])

print(p.x, p.y, p.z)

for value in p:
    print(value)

temp_list = [1,2,3]
n_t = named_tuple._make(temp_list)
print(n_t)

['b'] ['b']
1 2 3
1 2 3
1
2
3
Point(x=1, y=2, z=3)

기존 tuple 을 사용하기 위해서는 위치 인덱스를 사용했어야 했다. 실제 사용할만한 예시 상황을 보면 다음과 같다.

EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade')

import csv
for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))):
    print(emp.name, emp.title)

import sqlite3
conn = sqlite3.connect('/companydata')
cursor = conn.cursor()
cursor.execute('SELECT name, age, title, department, paygrade FROM employees')
for emp in map(EmployeeRecord._make, cursor.fetchall()):
    print(emp.name, emp.title)

다음과 같이 tuple 방식의 결과물을 받을 때 유용하게 사용할 수 있다. 이외에도 Sqlalchemy 를 통한 return 값을 받을 때도 namedtuple을 쓰면 보다 직관적이고 효율적인 코드 작성이 가능하다.

profile
Developer
post-custom-banner

0개의 댓글