ฉันเห็นตัวอย่างของโค้ดที่hash
ใช้ฟังก์ชันกับทูเพิล ด้วยเหตุนี้จึงส่งคืนจำนวนเต็มลบ ฉันสงสัยว่าฟังก์ชั่นนี้ทำหน้าที่อะไร? Google ไม่ช่วย ฉันพบหน้าที่อธิบายวิธีคำนวณแฮช แต่ไม่ได้อธิบายว่าทำไมเราถึงต้องการฟังก์ชันนี้
ฉันเห็นตัวอย่างของโค้ดที่hash
ใช้ฟังก์ชันกับทูเพิล ด้วยเหตุนี้จึงส่งคืนจำนวนเต็มลบ ฉันสงสัยว่าฟังก์ชั่นนี้ทำหน้าที่อะไร? Google ไม่ช่วย ฉันพบหน้าที่อธิบายวิธีคำนวณแฮช แต่ไม่ได้อธิบายว่าทำไมเราถึงต้องการฟังก์ชันนี้
คำตอบ:
กัญชาเป็นจำนวนเต็มขนาดคงที่ที่ระบุค่าเฉพาะ แต่ละค่าต้องมีแฮชของตัวเองดังนั้นสำหรับค่าเดียวกันคุณจะได้รับแฮชเดียวกันแม้ว่าจะไม่ใช่อ็อบเจกต์เดียวกันก็ตาม
>>> hash("Look at me!")
4343814758193556824
>>> f = "Look at me!"
>>> hash(f)
4343814758193556824
ต้องสร้างค่าแฮชในลักษณะที่กระจายค่าผลลัพธ์อย่างเท่าเทียมกันเพื่อลดจำนวนการชนกันของแฮชที่คุณได้รับ การชนกันของแฮชคือเมื่อค่าสองค่าที่แตกต่างกันมีแฮชเหมือนกัน ดังนั้นการเปลี่ยนแปลงที่ค่อนข้างเล็กมักส่งผลให้แฮชต่างกันมาก
>>> hash("Look at me!!")
6941904779894686356
ตัวเลขเหล่านี้มีประโยชน์มากเนื่องจากสามารถค้นหาค่าได้อย่างรวดเร็วในคอลเล็กชันค่าจำนวนมาก สองตัวอย่างของการใช้งานคือ Python set
และdict
. ในlist
ถ้าคุณต้องการที่จะตรวจสอบว่ามีค่าอยู่ในรายการที่มีif x in values:
งูใหญ่ต้องผ่านรายการทั้งหมดและเปรียบเทียบกับค่าในแต่ละรายการx
นี้สามารถใช้เวลานานเป็นเวลานานvalues
list
ในset
Python จะติดตามแฮชแต่ละตัวและเมื่อคุณพิมพ์if x in values:
Python จะได้รับค่าแฮชสำหรับx
ค้นหาในโครงสร้างภายในจากนั้นเปรียบเทียบx
กับค่าที่มีแฮชเดียวกันx
เท่านั้น
วิธีการเดียวกันนี้ใช้สำหรับการค้นหาพจนานุกรม ทำให้การค้นหาset
และdict
รวดเร็วมากในขณะที่การค้นหาlist
ช้า นอกจากนี้ยังหมายความว่าคุณสามารถมีอ็อบเจ็กต์ที่ไม่สามารถแฮชได้ใน a list
แต่ไม่มีใน a set
หรือเป็นคีย์ในไฟล์dict
. ตัวอย่างทั่วไปของอ็อบเจ็กต์ที่ไม่สามารถแฮชได้คืออ็อบเจ็กต์ใด ๆ ที่ไม่แน่นอนซึ่งหมายความว่าคุณสามารถเปลี่ยนค่าได้ หากคุณมีอ็อบเจกต์ที่เปลี่ยนแปลงได้ไม่ควรแฮชเนื่องจากแฮชของมันจะเปลี่ยนไปตลอดอายุการใช้งานซึ่งจะทำให้เกิดความสับสนอย่างมากเนื่องจากอ็อบเจ็กต์อาจอยู่ภายใต้ค่าแฮชที่ไม่ถูกต้องในพจนานุกรม
โปรดทราบว่าแฮชของค่าจะต้องเหมือนกันสำหรับการรัน Python เพียงครั้งเดียว ใน Python 3.3 จริง ๆ แล้วพวกเขาจะเปลี่ยนทุกครั้งที่รัน Python ใหม่:
$ /opt/python33/bin/python3
Python 3.3.2 (default, Jun 17 2013, 17:49:21)
[GCC 4.6.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hash("foo")
1849024199686380661
>>>
$ /opt/python33/bin/python3
Python 3.3.2 (default, Jun 17 2013, 17:49:21)
[GCC 4.6.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hash("foo")
-7416743951976404299
สิ่งนี้จะทำให้ยากที่จะคาดเดาว่าค่าแฮชที่สตริงหนึ่ง ๆ จะมีค่าอะไรซึ่งเป็นคุณสมบัติด้านความปลอดภัยที่สำคัญสำหรับเว็บแอปพลิเคชันเป็นต้น
ดังนั้นจึงไม่ควรเก็บค่าแฮชไว้อย่างถาวร หากคุณจำเป็นต้องใช้ค่าแฮชอย่างถาวรคุณสามารถดูประเภทแฮชที่ "ร้ายแรง" มากขึ้นฟังก์ชันแฮชการเข้ารหัสที่สามารถใช้ในการสร้างเช็คซัมของไฟล์ที่ตรวจสอบได้เป็นต้น
hash(-1) == hash(-2)
(runnin Python 2.7)
hash(-1) == hash(-2)
ยังคงมีอยู่ในปัจจุบัน โชคดีที่มันไม่ส่งผลเสียต่อพจนานุกรมและการตั้งค่าการค้นหา ทั้งหมดจำนวนเต็มอื่น ๆi
การแก้ปัญหาให้ตัวเองสำหรับการยกเว้นhash(i)
-1
โปรดดูอภิธานศัพท์ : hash()
ใช้เป็นทางลัดในการเปรียบเทียบอ็อบเจกต์โดยอ็อบเจ็กต์จะถือว่าแฮชได้หากเปรียบเทียบกับอ็อบเจ็กต์อื่น hash()
นั่นคือเหตุผลที่เราใช้ นอกจากนี้ยังใช้ในการเข้าถึงdict
และset
องค์ประกอบที่จะดำเนินการตามตารางกัญชาปรับขนาดได้ใน CPython
hash()
ฟังก์ชั่นเป็นลำดับความสำคัญ (หรือหลายอย่าง) ที่มีราคาไม่แพงหากคุณอ่านเกี่ยวกับวิธีการนำพจนานุกรมมาใช้พวกเขาใช้ตารางแฮชซึ่งหมายความว่าการได้มาซึ่งคีย์จากอ็อบเจกต์นั้นเป็นมุมหินในการดึงอ็อบเจกต์ในพจนานุกรมในO(1)
. ว่า แต่มากขึ้นอยู่กับฟังก์ชันแฮชของคุณจะมีการปะทะกันทน กรณีที่เลวร้ายที่สุดสำหรับการรับรายการO(n)
ในพจนานุกรมเป็นจริง
ในหมายเหตุนั้นโดยปกติแล้ววัตถุที่เปลี่ยนแปลงไม่ได้ คุณสมบัติที่แฮชได้หมายความว่าคุณสามารถใช้อ็อบเจกต์เป็นคีย์ได้ หากใช้ค่าแฮชเป็นคีย์และเนื้อหาของอ็อบเจ็กต์เดียวกันนั้นเปลี่ยนไปฟังก์ชันแฮชควรจะคืนค่าอะไร เป็นคีย์เดียวกันหรือคนละคีย์? มันขึ้นอยู่กับว่าคุณกำหนดฟังก์ชันแฮชของคุณ
ลองนึกภาพว่าเรามีคลาสนี้:
>>> class Person(object):
... def __init__(self, name, ssn, address):
... self.name = name
... self.ssn = ssn
... self.address = address
... def __hash__(self):
... return hash(self.ssn)
... def __eq__(self, other):
... return self.ssn == other.ssn
...
โปรดทราบ: ทั้งหมดนี้เป็นไปตามสมมติฐานที่ว่า SSN ไม่เคยเปลี่ยนแปลงสำหรับแต่ละบุคคล (ไม่รู้ด้วยซ้ำว่าจะตรวจสอบข้อเท็จจริงนั้นจากแหล่งที่เชื่อถือได้ที่ไหน)
และเรามี Bob:
>>> bob = Person('bob', '1111-222-333', None)
บ็อบไปพบผู้พิพากษาเพื่อเปลี่ยนชื่อ:
>>> jim = Person('jim bo', '1111-222-333', 'sf bay area')
นี่คือสิ่งที่เรารู้:
>>> bob == jim
True
แต่นี่เป็นวัตถุสองชิ้นที่แตกต่างกันโดยมีการจัดสรรหน่วยความจำที่แตกต่างกันเช่นเดียวกับบันทึกที่แตกต่างกันสองรายการของบุคคลเดียวกัน:
>>> bob is jim
False
มาถึงส่วนที่แฮช () มีประโยชน์:
>>> dmv_appointments = {}
>>> dmv_appointments[bob] = 'tomorrow'
เดาอะไร:
>>> dmv_appointments[jim] #?
'tomorrow'
จากบันทึกที่แตกต่างกันสองรายการคุณสามารถเข้าถึงข้อมูลเดียวกันได้ ลองทำสิ่งนี้:
>>> dmv_appointments[hash(jim)]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in __eq__
AttributeError: 'int' object has no attribute 'ssn'
>>> hash(jim) == hash(hash(jim))
True
เกิดอะไรขึ้น? นั่นคือการปะทะกัน เนื่องจากhash(jim) == hash(hash(jim))
ซึ่งเป็นจำนวนเต็ม btw ทั้งคู่เราจึงต้องเปรียบเทียบอินพุตของ__getitem__
รายการทั้งหมดที่ชนกัน builtin int
ไม่มีssn
แอตทริบิวต์จึงเดินทาง
>>> del Person.__eq__
>>> dmv_appointments[bob]
'tomorrow'
>>> dmv_appointments[jim]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: <__main__.Person object at 0x7f611bd37110>
ในตัวอย่างสุดท้ายนี้ฉันแสดงให้เห็นว่าแม้จะมีการชนกัน แต่การเปรียบเทียบจะดำเนินการ แต่วัตถุก็ไม่เท่ากันอีกต่อไปซึ่งหมายความว่ามันสามารถยก a KeyError
.
hash()
เป็นจำนวนเต็มขนาดคงที่ซึ่งอาจทำให้เกิดการชนกันได้
__eq__
ในตัวอย่างด้านบนได้ พจนานุกรมเรียกว่าเมื่อพยายามเปรียบเทียบคีย์ที่ได้รับกับคีย์ทั้งหมดที่มีหรือไม่? ดังกล่าวว่าโดยวิธีการในตัวอย่างที่ผ่านมาในพจนานุกรมมีอะไรจะโทรเพื่อใช้ในการตรวจสอบเทียบเท่าของคีย์จะได้รับการต้อนรับด้วยกุญแจจะมีหรือไม่ del
__eq__
hash(jim)
อธิบายไม่สมบูรณ์ในตัวอย่างที่มีคีย์ Person.__eq__
ถูกเรียกเนื่องจากคีย์ที่มีอยู่มีแฮชเหมือนกันhash(jim)
เพื่อให้แน่ใจว่าคีย์Person.__eq__
นี้ถูกใช้ มันผิดพลาดเพราะถือว่านั่นother
คือint
มีssn
แอตทริบิวต์ หากไม่มีhash(jim)
คีย์ในพจนานุกรม__eq__
จะไม่ถูกเรียก สิ่งนี้อธิบายว่าเมื่อการค้นหาคีย์สามารถเป็น O (n): เมื่อรายการทั้งหมดมีแฮชเดียวกัน__eq__
ต้องใช้กับรายการทั้งหมดเช่นในกรณีที่ไม่มีคีย์
dmv_appointments[bob.ssn] = 'tomorrow'
จำเป็นต้องกำหนด__hash__
วิธีการ ฉันเข้าใจว่าเพิ่มอักขระ 4 ตัวสำหรับการนัดหมายทุกครั้งที่คุณเขียนและอ่าน แต่ดูเหมือนว่าจะชัดเจนกว่าสำหรับฉัน
เอกสารhash()
Python สำหรับสถานะ:
ค่าแฮชเป็นจำนวนเต็ม ใช้เพื่อเปรียบเทียบคีย์พจนานุกรมอย่างรวดเร็วในระหว่างการค้นหาพจนานุกรม
พจนานุกรม Python ถูกนำไปใช้เป็นตารางแฮช ดังนั้นทุกครั้งที่คุณใช้พจนานุกรมระบบhash()
จะเรียกคีย์ที่คุณส่งผ่านเพื่อมอบหมายงานหรือค้นหา
นอกจากนี้เอกสารสำหรับdict
สถานะประเภท :
ค่าที่ไม่สามารถแฮชได้นั่นคือค่าที่มีรายการพจนานุกรมหรือประเภทอื่น ๆ ที่เปลี่ยนแปลงได้ (ซึ่งเปรียบเทียบตามค่าแทนที่จะเป็นเอกลักษณ์ของอ็อบเจ็กต์) ไม่สามารถใช้เป็นคีย์ได้
คุณสามารถใช้Dictionary
ชนิดข้อมูลใน python คล้ายกับแฮชมาก - และยังรองรับการซ้อนกันคล้ายกับแฮชที่ซ้อนกัน
ตัวอย่าง:
dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
dict['Age'] = 8; # update existing entry
dict['School'] = "DPS School" # Add new entry
print ("dict['Age']: ", dict['Age'])
print ("dict['School']: ", dict['School'])
สำหรับข้อมูลเพิ่มเติมโปรดอ้างอิงนี้สอนเกี่ยวกับชนิดข้อมูลพจนานุกรม