如何为自定义Contact类实现与__eq__匹配的哈希函数?
如何为自定义Contact类实现符合相等逻辑的哈希函数?
我正在开发一个用于检查不完整数据的程序,定义了如下Contact类:
class Contact: def __init__(self, name, phone, email): self.names = {name} if pd.notna(name) else set() self.phones = {parsed_phone} if (parsed_phone := _parse_phone(phone)) else set() self.emails = {parsed_email} if (parsed_email := _parse_email(email)) else set() def __eq__(self, other): return (other.names & self.names) + (other.phones & self.phones) + (other.emails & self.emails) >= 1
该类的__eq__方法规定:只要两个Contact实例的姓名、电话、邮箱任一数据项匹配,则二者相等。现需实现一个与该相等逻辑匹配的哈希函数,满足以下要求:
示例1:
a = Contact("James Smith", "+xx xxx xx xx xx", "james.smith@email.com") b = Contact("James Smit", "+xx xxx xx yx xx", "james.smith@email.com") # a == b 为 True,因此需要 hash(a) == hash(b)
示例2:
a = Contact("James Smith", "+xx xxx xx xx xx", "james.smith@email.com") b = Contact("Jane Doe", "+zz zzz zz zz zz", "jane.doe@email.com") # a == b 为 False,因此需要 hash(a) != hash(b)
请问该哈希函数应如何编写?
首先要明确:当前__eq__定义的相等关系不满足等价关系的传递性——比如实例a和b共享邮箱、b和c共享电话,但a和c没有任何重叠数据时,a == b和b == c都为True,可a == c为False。而Python哈希协议要求a == b时必须hash(a) == hash(b),这会导致hash(a) == hash(b) == hash(c),但a != c又要求hash(a) != hash(c),这是矛盾的。
因此严格来说,无法实现完全符合该__eq__逻辑的哈希函数,但可以实现满足你给出的示例场景的方案,或者调整__eq__逻辑使其符合等价关系要求。
方案1:基于所有属性的哈希异或(满足示例需求)
如果你的使用场景不会出现上述传递性冲突,或可接受由此带来的哈希碰撞(不同组实例可能有相同哈希),可以用以下方式实现__hash__:
def __hash__(self): # 收集所有非空标识项 all_ids = [] all_ids.extend(self.names) all_ids.extend(self.phones) all_ids.extend(self.emails) if not all_ids: return hash(()) # 对所有标识项的哈希值做异或运算 combined_hash = 0 for item in all_ids: combined_hash ^= hash(item) return combined_hash
为什么符合示例要求:
- 示例1中a和b共享同一个邮箱,二者的
all_ids集合包含该邮箱,异或运算中相同元素的哈希会抵消,最终组合哈希值相等。 - 示例2中a和b无任何共享元素,异或后的哈希值大概率不同。
方案2:调整相等逻辑为等价关系(符合Python规范)
若要严格遵循Python哈希协议,需修改__eq__使其成为等价关系——比如用并查集维护所有实例的分组,两个实例相等当且仅当属于同一分组。但这种方式需要全局状态管理分组,实现复杂度更高。
注意事项
- 方案1可能出现不同组实例哈希相同的情况,这是哈希函数的正常现象,Python允许这种碰撞,只要保证
a == b时hash(a) == hash(b)即可。 - 如果
Contact实例的names/phones/emails属性在创建后会被修改,不能将实例用于set或dict键,否则哈希值变化会导致集合/字典异常。
内容的提问来源于stack exchange,提问作者Simon Van den Bossche
相关产品推荐
相关产品推荐

