일반 튜플의 한계
튜플을 레코드로 사용할 때의 단점은 각 항목이 실제로 무엇을 의미하는지 알 수 없다는 점입니다. 개발자 본인만 알 뿐, API 제공자라면 호출자는 문서를 통해서만 각 인덱스가 무엇을 나타내는지 파악해야 합니다. 요구사항이 변경되어도 문서가 업데이트되지 않으면 쉽게 오류가 발생합니다. 값을 가져올 때마다 0번, 1번, 2번 인덱스를 사용해야 합니다.
namedtuple: 네임드튜플
더 발전된 형태의 튜플로 namedtuple이 있습니다. 이름 그대로 '이름을 가진 튜플'입니다. 즉, 인덱스로 접근하는 대신 필드명(이름)으로 직접 값을 가져올 수 있습니다.
from collections import namedtuple
# namedtuple 클래스 정의
User = namedtuple("User", 'name gender height weight')
# 또는 리스트로도 정의 가능
# User = namedtuple("User", ['name', 'gender', 'height', 'weight'])
# namedtuple 인스턴스 생성
user = User(name="jack", gender="female", height=170, weight=120)
user.name # 'jack'
user.gender # 'female'
namedtuple을 정의할 때 첫 번째 인자는 튜플의 이름입니다. 이는 클래스명과 유사합니다. 두 번째 인자는 공백으로 구분된 문자열(또는 문자열 리스트)로, 튜플의 필드 4개를 나타냅니다. 이는 클래스의 속성과 같습니다.
초기화 방식도 클래스 인스턴스와 동일합니다. 일반 클래스로 표현하면 다음과 같습니다:
class User:
def __init__(self, name, gender, height, weight):
self.name = name
self.gender = gender
self.height = height
self.weight = weight
user = User(name="jack", gender="female", height=170, weight=120)
user.name # 'jack'
비교해보면, namedtuple은 가벼운 클래스입니다. 코드가 더 간결하며, 속성만 있고 별도 메서드가 필요 없는 단순한 클래스라면 namedtuple로 대체할 수 있습니다. namedtuple은 일반 클래스보다 효율적이며, 내부적으로 유지할 요소가 적기 때문입니다.
namedtuple은 실제로 tuple을 상속받은 서브클래스입니다. 따라서 튜플의 특성(인덱스 접근, 슬라이싱 등)을 그대로 유지하며, 필드 값 재할당이 불가능합니다.
>>> user[1:3]
('female', 170)
>>> user[0]
'jack'
>>> user.name = 'bob'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
namedtuple이 일반 튜플보다 나은 점은 자기 설명적(self-descriptive)이라는 것입니다. 튜플은 문맥 없이 값을 해석하기 어렵지만, namedtuple은 각 필드가 무엇을 의미하는지 스스로 설명할 수 있습니다.