TL; DR:
Vui lòng tham khảo bảng chú giải thuật ngữ : hash()
được sử dụng như một phím tắt để so sánh các đối tượng, một đối tượng được coi là có thể băm nếu nó có thể được so sánh với các đối tượng khác. đó là lý do tại sao chúng tôi sử dụng hash()
. Nó cũng được sử dụng để truy cập dict
và set
các phần tử được triển khai dưới dạng bảng băm có thể thay đổi kích thước trong CPython .
Cân nhắc kỹ thuật
- thường thì việc so sánh các đối tượng (có thể liên quan đến một số cấp đệ quy) là tốn kém.
- tốt hơn là,
hash()
hàm là một cấp độ lớn (hoặc một số) ít tốn kém hơn.
- so sánh hai hàm băm dễ dàng hơn so sánh hai đối tượng, đây là vị trí của phím tắt.
Nếu bạn đọc về cách các từ điển được triển khai , chúng sử dụng bảng băm, có nghĩa là lấy một khóa từ một đối tượng là một tảng đá góc để lấy các đối tượng trong từ điển trong O(1)
. Tuy nhiên, điều đó rất phụ thuộc vào hàm băm của bạn để chống va chạm . Các trường hợp xấu nhất để có được một mục trong từ điển thực sự O(n)
.
Lưu ý rằng, các đối tượng có thể thay đổi thường không thể băm được. Thuộc tính có thể băm có nghĩa là bạn có thể sử dụng một đối tượng làm khóa. Nếu giá trị băm được sử dụng làm khóa và nội dung của cùng một đối tượng đó thay đổi, thì hàm băm sẽ trả về giá trị gì? Nó là cùng một khóa hay một khóa khác? Nó phụ thuộc vào cách bạn xác định hàm băm của mình.
Học theo ví dụ:
Hãy tưởng tượng chúng ta có lớp học này:
>>> 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
...
Xin lưu ý: tất cả đều dựa trên giả định rằng SSN không bao giờ thay đổi đối với một cá nhân (thậm chí không biết nơi thực sự xác minh thông tin đó từ nguồn có thẩm quyền).
Và chúng tôi có Bob:
>>> bob = Person('bob', '1111-222-333', None)
Bob đến gặp thẩm phán để đổi tên:
>>> jim = Person('jim bo', '1111-222-333', 'sf bay area')
Đây là những gì chúng tôi biết:
>>> bob == jim
True
Nhưng đây là hai đối tượng khác nhau được cấp phát bộ nhớ khác nhau, giống như hai bản ghi khác nhau của cùng một người:
>>> bob is jim
False
Bây giờ đến phần mà hash () rất hữu ích:
>>> dmv_appointments = {}
>>> dmv_appointments[bob] = 'tomorrow'
Đoán xem:
>>> dmv_appointments[jim]
'tomorrow'
Từ hai bản ghi khác nhau, bạn có thể truy cập cùng một thông tin. Bây giờ hãy thử điều này:
>>> 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
Chuyện gì vừa xảy ra? Đó là một vụ va chạm. Vì hash(jim) == hash(hash(jim))
cả hai đều là số nguyên btw, chúng ta cần so sánh đầu vào của __getitem__
với tất cả các mục xung đột. Nội trang int
không có một ssn
thuộc tính để nó di chuyển.
>>> 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>
Trong ví dụ cuối cùng này, tôi chỉ ra rằng ngay cả khi có va chạm, so sánh được thực hiện, các đối tượng không còn bằng nhau nữa, có nghĩa là nó đã nâng thành công a KeyError
.