본문으로 바로가기
TaeyoungKim.dev

파이썬 튜플은 불변인데 안의 리스트는 왜 바뀔까? append와 TypeError 차이

파이썬작성 약 3분 읽기TaeyoungKim
LinkedInX

수정되지 않게 하려고 설정을 튜플에 넣었다. 그런데 그 안의 작업 목록에 새 항목이 붙었다. '튜플은 불변'이라고 배웠는데, 목록은 왜 멀쩡히 바뀌었을까? 핵심은 튜플이 보관하는 자리와 그 자리가 가리키는 리스트를 구분하는 데 있다.

튜플의 원소를 다시 넣으면 왜 TypeError가 날까?

튜플은 만들어진 뒤 원소 자리에 다른 객체를 다시 넣을 수 없다. 아래의 record[0] 자리에는 steps라는 리스트 객체가 들어 있다.

python
steps = ["draft"]
record = (steps, "open")

record[0] = ["reviewed"]  # TypeError: 'tuple' object does not support item assignment

새 리스트로 자리 자체를 교체하려 했기 때문에 실패한다. record[1] = "closed"도 같은 이유로 실패한다. 여기까지가 튜플의 불변성이 보장하는 범위다.

튜플 안의 리스트에 append는 왜 성공할까?

앞의 재할당 줄은 빼고 같은 steps와 record로 다시 실행해 보자.

도식에서 바뀌는 것은 튜플의 원소 자리가 아니라 그 자리가 가리키는 리스트의 내용이다. append 전후의 튜플을 출력해 구분해 보자.

python
steps = ["draft"]
record = (steps, "open")

record[0].append("reviewed")
print(record)             # (['draft', 'reviewed'], 'open')
print(record[0] is steps) # True

record[0]을 다른 리스트로 바꾸지 않았다. 그 자리가 계속 같은 리스트를 가리키는 상태에서, 리스트의 내용만 바꿨다. 그래서 튜플은 여전히 같은 원소를 보관하지만 출력 결과는 달라진다. 튜플 바깥의 steps로 append()해도 record에서 변경이 보이는 이유 역시 둘이 같은 리스트를 공유하기 때문이다.

여기서 record[0] += ["approved"]는 피하자. 내부 리스트에는 값이 붙은 뒤에 튜플 자리에 결과를 다시 대입하려다 TypeError가 난다. 오류가 났으니 아무것도 바뀌지 않았으리라 믿으면 더 곤란하다. 이 경우도 독립 실행에서 리스트 변경과 오류가 함께 일어나는 것을 확인했다.

바깥을 튜플로 만들면 안쪽 리스트도 고정될까?

아니다. 바깥을 튜플로 바꿔도 그 안에 담긴 리스트는 그대로 가변이다. 값을 고정한 기록이 필요하다면 내부 리스트도 별도로 튜플로 변환해야 한다.

python
steps = ["draft"]
record = (steps, "open")
snapshot = (tuple(steps), "open")

steps.append("reviewed")
print(record)   # (['draft', 'reviewed'], 'open')
print(snapshot) # (('draft',), 'open')

snapshot은 그 시점의 문자열 목록을 별도 튜플로 담는다. 다만 안쪽에 또 가변 객체가 있다면 한 번의 tuple() 변환만으로 모든 중첩 내용이 고정되지는 않는다. 어떤 값까지 바뀌지 않아야 하는지부터 정해야 한다.

딕셔너리 키로 쓰려면?

리스트가 들어 있는 튜플은 해시할 수 없어서 딕셔너리 키로도 쓸 수 없다. 바깥이 튜플이라는 사실만으로 충분하지 않다. 앞의 snapshot처럼 안쪽도 문자열·튜플 등 해시 가능한 값으로 구성해야 한다. 공유된 리스트가 나중에 바뀌어도 원래 기록이 남아야 한다면, 키가 되느냐보다 그 시점의 값을 따로 보존했느냐가 먼저다.

핵심 요약

튜플의 불변성은 원소 자리의 재할당 금지다. 그 자리가 가리키는 리스트까지 자동으로 얼려 주지는 않는다. record[0] = ...은 실패하지만 record[0].append(...)는 같은 리스트의 내용을 바꾼다. 변경되지 않는 기록이 필요하면 안쪽 값까지 가변인지 확인하고 별도로 고정하자.

작성자

TaeyoungKim

기초 개념을 구현과 검증, 실제 운영 판단까지 연결해 기록합니다.

#Python tuple#파이썬 튜플 불변#파이썬 리스트#append#TypeError

함께 읽으면 좋은 글