목록은 변경 가능하기 때문에 dict
키 (및 set
멤버)는 해시 가능해야하며 변경 가능한 객체를 해싱하는 것은 해시 값 이 인스턴스 속성을 기반으로 계산 입니다.
이 답변에서는 기존 답변에 가치를 더하는 구체적인 예를 제공합니다. 모든 통찰력은set
데이터 구조 .
예제 1 : 해시 값이 객체의 변경 가능한 특성을 기반으로하는 변경 가능한 객체를 해싱합니다.
>>> class stupidlist(list):
... def __hash__(self):
... return len(self)
...
>>> stupid = stupidlist([1, 2, 3])
>>> d = {stupid: 0}
>>> stupid.append(4)
>>> stupid
[1, 2, 3, 4]
>>> d
{[1, 2, 3, 4]: 0}
>>> stupid in d
False
>>> stupid in d.keys()
False
>>> stupid in list(d.keys())
True
mutating 후에 stupid
는 해시가 변경 되었기 때문에 더 이상 dict에서 찾을 수 없습니다. dict의 키 목록에 대한 선형 스캔 만stupid
.
예 2 : ...하지만 왜 상수 해시 값이 아닌가?
>>> class stupidlist2(list):
... def __hash__(self):
... return id(self)
...
>>> stupidA = stupidlist2([1, 2, 3])
>>> stupidB = stupidlist2([1, 2, 3])
>>>
>>> stupidA == stupidB
True
>>> stupidA in {stupidB: 0}
False
그것은 좋은 생각이 아닙니다. 동일한 객체는 동일하게 해시하여 dict
또는set
.
예 3 : ... 좋아, 모든 인스턴스에서 상수 해시는 어떻습니까?!
>>> class stupidlist3(list):
... def __hash__(self):
... return 1
...
>>> stupidC = stupidlist3([1, 2, 3])
>>> stupidD = stupidlist3([1, 2, 3])
>>> stupidE = stupidlist3([1, 2, 3, 4])
>>>
>>> stupidC in {stupidD: 0}
True
>>> stupidC in {stupidE: 0}
False
>>> d = {stupidC: 0}
>>> stupidC.append(5)
>>> stupidC in d
True
예상대로 작동하는 것 같지만 무슨 일이 일어나고 있는지 생각해보십시오. 클래스의 모든 인스턴스가 동일한 해시 값을 생성하면 두 개 이상의 인스턴스가 하나의 키로 존재할 때마다 해시 충돌이 발생합니다. dict
합니다 set
.
my_dict[key]
또는 key in my_dict
(또는 item in my_set
)을 사용 하여 올바른 인스턴스를 찾으려면stupidlist3
찾으려면 dict의 키에있는 (최악의 경우) . 이 시점에서 사전의 목적인 O (1) 조회는 완전히 실패합니다. 이것은 다음 타이밍에서 시연됩니다 (IPython으로 수행됨).
예제 3의 몇 가지 타이밍
>>> lists_list = [[i] for i in range(1000)]
>>> stupidlists_set = {stupidlist3([i]) for i in range(1000)}
>>> tuples_set = {(i,) for i in range(1000)}
>>> l = [999]
>>> s = stupidlist3([999])
>>> t = (999,)
>>>
>>> %timeit l in lists_list
25.5 µs ± 442 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit s in stupidlists_set
38.5 µs ± 61.2 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit t in tuples_set
77.6 ns ± 1.5 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
보시다시피, 우리의 멤버십 테스트 stupidlists_set
는 전체에 대한 선형 스캔보다 훨씬 느리지 만 lists_list
해시 충돌 부하없이 세트에서 예상되는 초고속 조회 시간 (팩터 500)이 있습니다.
TL; DR : 당신은 사용할 수 있습니다 tuple(yourlist)
로 dict
튜플 불변 해쉬 때문에, 키.