먼저 요약
list : 내용 기반인데 내용이 변함 → hash 불안정 → 막힘
tuple : 내용 기반인데 내용이 안 변함 → hash 안정 → OK
일반 객체 : id 기반이고 id는 안 변함 → hash 안정 → OK
먼저, hashable 관점에서의 Tuple에 대한 설명은 아래를 참조
hashable의 조건은 아래와 같음:
hashable의 필수 조건
1. "해시값이 **객체 수명 동안 안정적**일 것"
2. "**== 기준과 hash 기준이 일치**할 것"
불변성은 이 조건을 만족시키는 흔한 수단일 뿐, 필수 조건은 아님
이로 인해서, 가변 객체도 hashable일 수 있음
list가 unhashable인 이유
일단 기본적으로 list의 == 는 “내용 기반”
이로 인해서, [1, 2] == [1, 2] → True
그리고 hashable 규약상 ==가 같으면 hash도 동일해야 함
근데 list는 내용이 변하는 특성을 가짐 (mutable)
이로 인해서 내용이 변경이 되면 hash도 변경이 되는 문제가 발생
→ 이로 인해서, 위에 hashable의 조건 2번이 깨짐
이러면 해시값이 불안정하면 저장 위치를 잃어버리고, 해당 위치에 대해서 찾아갈 수 없는 상황이 발생
따라서 Python은 list를 아예 unhashable로 막음
→ hash([1, 2]) # TypeError: unhashable type: ‘list”
그러면 일반 객체처럼 “id 기반”으로 만들면 되는거 아닌가?
기술적으로 가능하지만 하지 않음. 그러한 이유는:
a = [1, 2]
b = [1, 2]
# list를 id 기반 hash로 만들면:
hash(a) # a의 주소 -> EX. 1000
hash(b) # b의 주소 -> EX. 2000 <- 다름
a == b # Ture (내용 기반은 그대로 유지)
이로 인한 hashable에 대한 모순이 발생:
a == b 는 True 인데
hash(a) != hash(b) -> **규칙 위반**
여기서 의문점, 그러면 list를 왜 이렇게 설계가 되었는지?
list는 계속 추가/삭제/수정/정렬하는 가변 컬렉션으로 쓰라고 만든 자료구조
“값이 같은지 비교”하고 “자유롭게 변경”하는 게 핵심 용도
이 두 성질(내용 기반 ==과 가변 )이 합쳐지면서 구조적으로 hashable이 불가능해짐
그래서 키가 필요하면 tuple을 쓰라는 분업 구조로 설계
일반 객체는 == 가 주소 비교
일반 객체 두 개를 비교하면:
class User:
def __init__(self, name):
self.name = name
a = User("kim")
b = User("kim")
a == b # False -> 내용은 동일하나 False
False가 나오는 이유는 일반 객체의 == 는 “내용이 같냐”가 아닌 “같은 객체(주소가 같냐)”를 봄
a == a # True (같은 객체, 주소 동일)
a == b # False (다른 객체, 주소 다름)
hash 도 동일하게 “주소 기반”
hash(a) # a의 주소 기반 -> EX. 1000
hash(b) # b의 주소 기반 -> EX. 2000