John Millikin đã đề xuất một giải pháp tương tự như thế này:
class A(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
def __eq__(self, othr):
return (isinstance(othr, type(self))
and (self._a, self._b, self._c) ==
(othr._a, othr._b, othr._c))
def __hash__(self):
return hash((self._a, self._b, self._c))
Vấn đề với giải pháp này là hash(A(a, b, c)) == hash((a, b, c))
. Nói cách khác, hàm băm va chạm với bộ dữ liệu của các thành viên chủ chốt. Có lẽ điều này không quan trọng lắm trong thực tế?
Cập nhật: các tài liệu Python hiện khuyên bạn nên sử dụng một tuple như trong ví dụ trên. Lưu ý rằng các tài liệu nêu
Thuộc tính bắt buộc duy nhất là các đối tượng so sánh bằng nhau có cùng giá trị băm
Lưu ý rằng điều ngược lại là không đúng sự thật. Các đối tượng không so sánh bằng nhau có thể có cùng giá trị băm. Một va chạm băm như vậy sẽ không làm cho một đối tượng thay thế một đối tượng khác khi được sử dụng làm khóa chính hoặc đặt phần tử miễn là các đối tượng không so sánh bằng nhau .
Giải pháp lỗi thời / xấu
Các tài liệu Python trên__hash__
gợi ý kết hợp băm của các tiểu hợp phần sử dụng một cái gì đó giống như XOR , mang đến cho chúng ta điều này:
class B(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
def __eq__(self, othr):
if isinstance(othr, type(self)):
return ((self._a, self._b, self._c) ==
(othr._a, othr._b, othr._c))
return NotImplemented
def __hash__(self):
return (hash(self._a) ^ hash(self._b) ^ hash(self._c) ^
hash((self._a, self._b, self._c)))
Cập nhật: như Blckknght chỉ ra, việc thay đổi thứ tự của a, b và c có thể gây ra vấn đề. Tôi đã thêm một bổ sung ^ hash((self._a, self._b, self._c))
để nắm bắt thứ tự của các giá trị được băm. Cuối cùng này ^ hash(...)
có thể được loại bỏ nếu các giá trị được kết hợp không thể được sắp xếp lại (ví dụ: nếu chúng có các loại khác nhau và do đó giá trị của _a
sẽ không bao giờ được gán cho _b
hoặc _c
, v.v.).
__key
chức năng, đây là tốc độ nhanh nhất có thể. Chắc chắn, nếu các thuộc tính được biết là số nguyên và không có quá nhiều trong số chúng, tôi cho rằng bạn có khả năng có thể chạy nhanh hơn một chút với một số hàm băm cuộn tại nhà, nhưng có khả năng nó sẽ không được phân phối tốt.hash((self.attr_a, self.attr_b, self.attr_c))
sẽ nhanh đến mức đáng ngạc nhiên (và chính xác ), vì việc tạo ra cáctuple
s nhỏ được tối ưu hóa đặc biệt và nó đẩy công việc nhận và kết hợp các giá trị băm đến các nội trang C, thường nhanh hơn mã cấp Python.