John Millikin mengusulkan solusi yang mirip dengan ini:
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))
Masalah dengan solusi ini adalah bahwa hash(A(a, b, c)) == hash((a, b, c))
. Dengan kata lain, hash bertabrakan dengan hasrat anggota kunci. Mungkin ini tidak terlalu penting dalam praktek?
Perbarui: dokumen Python sekarang merekomendasikan untuk menggunakan tuple seperti pada contoh di atas. Perhatikan bahwa dokumentasi menyatakan
Satu-satunya properti yang diperlukan adalah objek yang membandingkan sama memiliki nilai hash yang sama
Perhatikan bahwa yang terjadi adalah sebaliknya. Objek yang tidak membandingkan sama mungkin memiliki nilai hash yang sama. Tabrakan hash seperti itu tidak akan menyebabkan satu objek untuk menggantikan yang lain ketika digunakan sebagai kunci dict atau mengatur elemen selama objek tidak juga sama .
Solusi usang / buruk
The dokumentasi Python pada__hash__
menyarankan untuk menggabungkan hash dari sub-komponen menggunakan sesuatu seperti XOR , yang memberi kami ini:
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)))
Pembaruan: seperti yang ditunjukkan Blckknght, mengubah urutan a, b, dan c dapat menyebabkan masalah. Saya menambahkan tambahan ^ hash((self._a, self._b, self._c))
untuk menangkap urutan nilai yang di-hash. Final ini ^ hash(...)
dapat dihapus jika nilai-nilai yang digabungkan tidak dapat disusun ulang (misalnya, jika mereka memiliki tipe yang berbeda dan karena itu nilai _a
tidak akan pernah ditugaskan _b
atau _c
, dll.).
__key
fungsi, ini tentang secepat hash apa pun. Tentu saja, jika atributnya dikenal sebagai bilangan bulat, dan jumlahnya tidak terlalu banyak, saya kira Anda bisa berjalan sedikit lebih cepat dengan beberapa hash buatan rumah, tetapi kemungkinan tidak akan terdistribusi dengan baik.hash((self.attr_a, self.attr_b, self.attr_c))
akan menjadi sangat cepat (dan benar ), karena pembuatantuple
s kecil dioptimalkan secara khusus, dan mendorong pekerjaan mendapatkan dan menggabungkan hash ke C builtin, yang biasanya lebih cepat daripada kode level Python.