파이썬 튜플은 Immutable Sequence이다. 그러나, 튜플에는 복합 타입의 원소들을 다룰 수 있고, 이에 대해 += 연산자를 사용하였을 때 이상한 일이 발생한다.
아래와 같은 (int, int, list[int]) 타입의 튜플에 += 연산자를 사용하면 무슨 일이 발생하겠는가?
>> t = (1, 2, [3, 4])
>> t[2] += [5, 6]
- t가 (1, 2, [3, 4, 5, 6])가 된다.
- 튜플 객체는 item assignment 연산을 지원하지 않는다는 에러메세지와 함께 타입에러 발생
놀랍게도 두가지 일이 동시에 일어난다.
>> t = (1, 2, [3, 4])
>> t[2] += [5, 6]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>> t
(1, 2, [3, 4, 5, 6])
왜 이런 결과가 발생했을까?
s[a] += b 표현식에 대해 파이썬 바이트 코드를 뜯어보면 그 원인을 파악할 수 있다.
>> import dis
>> dis.dis('s[a] += b')
1 0 LOAD_NAME 0 (s)
2 LOAD_NAME 1 (a)
4 DUP_TOP_TWO
6 BINARY_SUBSCR
8 LOAD_NAME 2 (b)
10 INPLACE_ADD
12 ROT_THREE
14 STORE_SUBSCR
16 LOAD_CONST 0 (None)
18 RETURN_VALUE
2 BINARY_SUBSCR: s[a] 값을 스택의 꼭대기(TOS)에 놓는다.
10 INPLACE_ADD: TOS += b를 수행한다. TOS가 여기서 Mutable Sequence를 가리키면 이 연산은 성공한다.
14 STORE_SUBSCR: TOS를 s[a]에 할당한다. s가 Immutable Sequence를 가리키면 이 연산은 실패한다.
물론, 이 경우는 흔하지 않은 케이스지만 여기서 얻을 수 있는 교훈은 다음과 같다.
- 튜플에 가변 배열을 넣는 일 따위는 애초에 하지 말아야 한다는 것
- 복합할당은 atomic한 연산이 아니다. (연산이 수행한 후 TOS의 할당단계에서 일어나는 예외케이스를 보면)