C.W.K.
Stream
Lesson 03 of 07 · published

Tuple — 불변성, unpacking, namedtuple

~20 min · tuple, immutable, unpacking, namedtuple

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

tuple 은 *바꿀 수 없는 list* 가 아니야

초보가 처음 듣는 tuple 의 정의 — "list 인데 못 바꾼다" — 는 핵심을 놓쳐. tuple 이 존재하는 이유는 다른 모양을 표현하기 위해서야. 고정된 모양의 레코드. list 는 "같은 종류 여러 개 순서대로" (주문 list, 사용자 list). tuple 은 "한 가지의 부분들" (좌표의 x/y, 데이터베이스 한 행). 모양이 달라. 그리고 Python 은 두 모양 다 idiomatic 하게 써.

tuple 만드는 법 — 진짜 연산자는 콤마

tuple 주변에 늘 보이는 괄호는 옵션 이야. tuple 을 만드는 건 콤마. x = 1, 2, 3 이 tuple. x = (1, 2, 3) 이 같은 tuple — 괄호는 단지 모호함을 없애줘. 예외 — 빈 tuple 은 () 가 필요하고, 원소 하나짜리는 끝에 콤마 ((1,)) 가 있어야 돼. 콤마 없으면 그냥 괄호 친 표현식.

Unpacking — Python 의 조용한 초능력

tuple unpacking 은 한 번 익히면 모든 곳에서 쓰게 되는 기능. x, y = 1, 2 가 한 줄 swap. 함수가 여러 값 반환하는 것도 사실 tuple 반환이야. tuple 을 yield 하는 iterator 는 for 안에서 unpacking 가능. * 가 들어간 first, *rest = my_list 는 "나머지 전부" 를 list 로.

불변성 — 무슨 뜻인지 정확히

tuple 의 원소를 재할당 못 해. t[0] = 99TypeError. 근데 tuple 안에 mutable 객체가 있으면, 그 객체는 여전히 mutable. t = ([1, 2], 3)t[0] 을 다른 list 로 바꿀 순 없지만 t[0].append(99) 는 가능. tuple 의 *구조* 는 얼었어도 *내용* 은 안 얼었을 수 있어.

원칙: tuple 이 hashable (dict 키 / set 원소로 사용 가능) 하려면 모든 원소가 각각 hashable 해야 돼. (1, 2, 3) 은 키 OK. ([1, 2], 3) 은 X — 안의 list 가 체인을 끊어.

namedtuple — tuple 에 이름 붙이기

tuple 을 위치로 인덱싱하는 건 잘 안 읽혀. row[3] 만 봐서는 3 이 뭔지 모르잖아. collections.namedtuple 은 각 필드에 이름이 붙은 tuple 서브클래스를 줘. tuple 의 모든 성질 (불변, hashable, 인덱싱, unpacking) 다 유지하면서 row.email 같은 접근도 가능. 더 현대적인 옵션 — typing.NamedTuple + 타입 힌트, dataclass(frozen=True) — 는 OOP 트랙에서. namedtuple 은 여전히 가볍고 빠른 첫 단추.

Code

tuple 만드는 법 — 그리고 원소 하나짜리 함정·python
# 모두 tuple — 콤마가 진짜 연산자
empty = ()
single = (1,)              # 끝 콤마 필수
pair = 1, 2                # 괄호 옵션
triple = (1, 2, 3)
mixed = "a", 1, [2, 3]     # tuple 안에 뭐든

# 끝 콤마 없으면 그냥 괄호 친 식
not_a_tuple = (1)
print(type(not_a_tuple))   # <class 'int'>
print(type(single))        # <class 'tuple'>

# tuple() 생성자 — 어떤 iterable 도
t = tuple("abc")
print(t)                   # ('a', 'b', 'c')
Unpacking — 일상의 초능력·python
# 다중 할당
x, y, z = 1, 2, 3

# swap
a, b = 10, 20
a, b = b, a
print(a, b)                # 20 10

# 여러 값 반환하는 함수도 사실 tuple 반환
def divmod_(a, b):
    return a // b, a % b

q, r = divmod_(17, 5)
print(q, r)                # 3 2

# star-unpacking — 나머지 잡기
first, *middle, last = [1, 2, 3, 4, 5]
print(first, middle, last) # 1 [2, 3, 4] 5
tuple 을 키로 — 단, 모든 원소가 hashable 일 때만·python
# hashable 한 것들의 tuple 은 hashable
grid = {}
grid[(0, 0)] = "start"
grid[(3, 4)] = "goal"
print(grid[(3, 4)])         # 'goal'

# list 가 들어간 tuple 은 hashable X
bad_key = (1, [2, 3])
try:
    grid[bad_key] = "oops"
except TypeError as e:
    print("실패:", e)        # unhashable type: 'list'
불변성 ≠ 깊이 얼었다·python
t = ([1, 2], 3)

# t[0] 자체는 재할당 불가
try:
    t[0] = [99]
except TypeError as e:
    print(e)                # 'tuple' object does not support item assignment

# 근데 t[0] 이 가리키는 객체는 mutable
t[0].append(99)
print(t)                    # ([1, 2, 99], 3)

# 그래서 이 tuple 은 hashable 도 아님 — 안의 list 가 깨
try:
    hash(t)
except TypeError as e:
    print(e)                # unhashable type: 'list'
namedtuple — tuple 의 모든 것 + 이름·python
from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)

print(p.x, p.y)             # 3 4    — 이름 접근
print(p[0], p[1])           # 3 4    — 인덱싱도 됨

x, y = p                    # unpacking 도 됨
print(x, y)                 # 3 4

# 불변
try:
    p.x = 99
except AttributeError as e:
    print(e)                # can't set attribute

# _replace — 새 namedtuple 반환
p2 = p._replace(x=10)
print(p2)                   # Point(x=10, y=4)

External links

Exercise

namedtupleStock 정의 — 필드는 ticker, price, shares. 세 종목 만들어. (a) 각 보유 가치 (price × shares) 출력. (b) 총 가치 내림차순 정렬. (c) _replace 로 한 종목 가격이 10% 떨어진 새 tuple 만들기 — 원본은 그대로. 마지막에 원본 출력해서 확인.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.