이 글에서는 본래 불변(immutable) 객체인 튜플(tuple)의 내부 값을 갱신하려고 할 때 어떤 일이 벌어지는지 살펴봅니다. 겉보기에는 새로운 값과 기존 값이 잘 병합되는 것처럼 보이지만, 실제로는 오류가 발생합니다. 오류가 발생하는 과정을 바이트코드(bytecode) 단계에서 분석해 보면 튜플의 동작 규칙을 한층 깊이 이해할 수 있습니다.
먼저 튜플을 정의한 뒤, 마지막 요소를 갱신하는 명령을 실행해 보겠습니다.
예제
>>> tupl = (5,7,9,[1,4]) >>> tupl[3] += [6,8]
실행 결과
Traceback (most recent call last): File "", line 1, in TypeError: 'tuple' object does not support item assignment >>> tupl (5, 7, 9, [1, 4, 6, 8])
흥미로운 점은 TypeError가 발생했음에도 튜플을 다시 출력해 보면 마지막 리스트에 [6, 8]이 반영되어 있다는 사실입니다. 즉, 덧셈 연산 자체는 성공했지만 그 결과를 튜플에 다시 저장하는 단계에서 실패한 것입니다.
바이트코드 분석
위 과정을 더 자세히 들여다보기 위해 코드를 함수로 감싼 후 dis 모듈을 사용해 바이트코드를 출력해 보겠습니다. 동일한 오류가 발생하지만, 이번에는 훨씬 많은 세부 정보를 확인할 수 있습니다.
예제
def immut_function(): tupl = (5,7,9,[1,4]) tupl[3] += [6,8] immut_function()
실행 결과
TypeError Traceback (most recent call last) in () 2 tupl = (5,7,9,[1,4]) 3 tupl[3] += [6,8] ----> 4 immut_function() in immut_function() 1 def immut_function(): 2 tupl = (5,7,9,[1,4]) ----> 3 tupl[3] += [6,8] 4 immut_function() TypeError: 'tuple' object does not support item assignment
다음으로 함수의 바이트코드를 출력하여 튜플 할당 과정과 오류가 발생하는 지점을 단계별로 확인해 보겠습니다.
예제
import dis dis.dis(immut_function)
실행 결과
2 0 LOAD_CONST 1 (5) 3 LOAD_CONST 2 (7) 6 LOAD_CONST 3 (9) 9 LOAD_CONST 4 (1) 12 LOAD_CONST 5 (4) 15 BUILD_LIST 2 18 BUILD_TUPLE 4 21 STORE_FAST 0 (tupl) 3 24 LOAD_FAST 0 (tupl) 27 LOAD_CONST 6 (3) 30 DUP_TOPX 2 33 BINARY_SUBSCR 34 LOAD_CONST 7 (6) 37 LOAD_CONST 8 (8) 40 BUILD_LIST 2 43 INPLACE_ADD 44 ROT_THREE 45 STORE_SUBSCR 46 LOAD_CONST 0 (None) 49 RETURN_VALUE
바이트코드의 주요 명령을 해석하면 다음과 같은 흐름을 알 수 있습니다.
- BINARY_SUBSCR: tupl[3]에 해당하는 값을 읽어 옵니다. 즉, 튜플 안에 들어 있는 리스트를 꺼냅니다.
- INPLACE_ADD: 꺼낸 리스트에 [6, 8]을 더합니다. 이 시점에서 리스트 자체는 실제로 성공적으로 변경됩니다.
- STORE_SUBSCR: 변경된 결과를 tupl[3] 위치에 다시 저장하려고 시도하지만, 튜플은 불변 객체이므로 바로 이 단계에서 TypeError가 발생합니다.
결론
결론은 간단합니다. 튜플 안에 가변(mutable) 객체를 넣지 않는 것이 좋습니다. 겉보기에는 값이 변경된 것처럼 보일 수 있지만, 실제로는 예외가 발생합니다. 위 바이트코드에서 확인할 수 있듯이 STORE_SUBSCR 단계에서 튜플은 불변 객체이기 때문에 항목 할당(item assignment)이 실패하게 됩니다. 또한 이러한 특성 때문에 튜플을 딕셔너리 키처럼 해시 가능(hashable)한 용도로 활용할 때도 내부에 리스트와 같은 가변 요소가 포함되어 있다면 예상치 못한 문제가 생길 수 있습니다. 따라서 튜플을 설계할 때는 반드시 불변 요소만 담는 습관을 들이는 것이 안전합니다.