เนื่องจากรายการสามารถเปลี่ยนแปลงได้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
หลังจากกลายพันธุ์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
คีย์ได้เนื่องจากทูเปิลไม่เปลี่ยนรูปและล้างทำความสะอาดได้