[Python] tuple: 불변 시퀀스와 NamedTuple
python tuple, 파이썬 튜플, namedtuple, tuple unpacking
정의
tuple은 불변(immutable) 시퀀스다. list와 거의 같은 API를 가지나 생성 후 변경 불가하며, 그 덕에 dict 키나 set 원소로 사용 가능하다(해시 가능). 함수의 다중 반환값, 좌표·레코드 같은 고정 구조에 흔히 쓰인다.
생성
empty = ()
single = (1,) # 콤마 필수! (1)는 그냥 괄호 묶인 표현식
pair = (1, 2)
triple = 1, 2, 3 # 괄호 없이도 됨 (tuple packing)
from_iter = tuple([1, 2, 3])
함정: (1)은 int, (1,)만 tuple.
python
print(type((1)))
print(type((1,)))
print(type(())) 결과
<class 'int'>
<class 'tuple'>
<class 'tuple'>불변성
t = (1, 2, 3)
t[0] = 99 # TypeError: 'tuple' object does not support item assignment
하지만 내부에 가변 객체가 있으면 그 객체는 변경 가능하다.
t = ([1, 2], [3, 4])
t[0].append(99) # 리스트 자체는 변경 가능
print(t) # ([1, 2, 99], [3, 4])
따라서 “tuple이 hashable이다”는 항상 참이 아니다. 내부에 list가 들어 있으면 hash(t)는 TypeError.
언패킹 (Tuple Unpacking)
가장 강력하고 흔히 쓰이는 기능.
python
a, b = 1, 2
print(a, b)
# 변수 교환
a, b = b, a
print(a, b)
# 다중 반환값
def minmax(xs):
return min(xs), max(xs)
lo, hi = minmax([3, 1, 4, 1, 5])
print(lo, hi)
# 스타 언패킹
first, *rest = (1, 2, 3, 4)
print(first, rest) 결과
1 2
2 1
1 5
1 [2, 3, 4]메서드 (불변이라 단 2개)
t = (1, 2, 3, 2, 1)
t.count(2) # 2
t.index(3) # 2
읽기 연산(len, in, 슬라이스, min/max/sum)은 list와 동일.
NamedTuple: 이름 있는 필드
collections.namedtuple 또는 typing.NamedTuple로 필드명을 부여한다. 작은 데이터 클래스가 필요할 때 dataclass보다 가볍다.
python
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p)
print(p.x, p.y)
print(p[0], p[1]) # 인덱스 접근도 가능
print(p._asdict()) # OrderedDict 변환
# 기존 값으로 새 인스턴스 (불변)
q = p._replace(x=99)
print(q) 결과
Point(x=3, y=4)
3 4
3 4
{'x': 3, 'y': 4}
Point(x=99, y=4)타입 힌트가 필요하면 typing.NamedTuple:
from typing import NamedTuple
class User(NamedTuple):
id: int
name: str
age: int = 0 # 기본값
u = User(1, "Alice")
tuple vs list vs dataclass vs NamedTuple
| 타입 | 가변 | 해시 | 필드명 | 메모리 | 용도 |
|---|---|---|---|---|---|
tuple | X | O | X | 최소 | 고정 레코드, 다중 반환 |
list | O | X | X | 보통 | 동적 컬렉션 |
namedtuple | X | O | O | 최소+ | 가벼운 불변 레코드 |
dataclass(frozen=True) | X | O | O | 보통 | 메서드/검증이 필요한 불변 데이터 |
dataclass | O | X | O | 보통 | 일반 데이터 클래스 |
hashable 활용
seen = set()
for x, y in points:
if (x, y) in seen:
continue
seen.add((x, y))
# dict 키
cache = {}
def fib(n):
if n in cache:
return cache[n]
cache[n] = fib(n - 1) + fib(n - 2) if n > 1 else n
return cache[n]
# 좌표를 dict 키로
grid = {(0, 0): "start", (1, 1): "wall"}
함수 리턴값 관용구
Python 함수는 사실상 항상 튜플을 반환한다(다중 값 = 튜플).
def stats(xs):
return min(xs), max(xs), sum(xs) / len(xs)
lo, hi, avg = stats([1, 2, 3, 4, 5])
result = stats([1, 2, 3]) # (1, 3, 2.0) 튜플
성능
- 메모리: tuple은 list보다 약 30% 작음 (over-allocation 없음).
- 생성: 컴파일 타임 상수 튜플은
LOAD_CONST로 즉시 적재(list는 매번 생성).
import dis
dis.dis("x = (1, 2, 3)") # LOAD_CONST (1, 2, 3)
dis.dis("x = [1, 2, 3]") # BUILD_LIST (3개 로드 + 빌드)
💬 댓글