Python重复实例校验失效:相同Amenity仍被添加至列表
问题排查:Amenity实例重复检测失效的原因及修复
问题描述
Room类的addAmenity方法意图检测重复的Amenity实例,若存在则返回"Duplicate found",否则添加至列表。但实际执行时,属性完全一致的room2Amenity4仍被添加,输出与预期不符。
原代码
class Amenity(): def __init__(self, itemCode, description, price): self._itemCode = itemCode self._description = description self._price = price def __str__(self): return "{}, {}, ${:.2f}".format(self._itemCode, self._description, self._price) class Room(): def __init__(self): self._amenities = [] def addAmenity(self, newItem): if newItem in self._amenities: return "Duplicate found" else: self._amenities.append(newItem) def __str__(self): amenityListing = "\n".join(str(amenity) for amenity in self._amenities) return "{} ".format(amenityListing) def main(): room2 = Room() room2Amenity1 = Amenity("GYM-PEP","Per entry pass to gym (Level 4-01)",1.00) room2Amenity2 = Amenity("FRIDGE","Mini Fridge (50L)",4.59) room2Amenity3 = Amenity("WI-FI","One-day Wi-Fi access",1.00) room2Amenity4 = Amenity("GYM-PEP","Per entry pass to gym (Level 4-01)",1.00) room2.addAmenity(room2Amenity1) room2.addAmenity(room2Amenity2) room2.addAmenity(room2Amenity3) print(room2) print() room2.addAmenity(room2Amenity4) print(room2)
实际输出
GYM-PEP, Per entry pass to gym (Level 4-01), $1.00 FRIDGE, Mini Fridge (50L), $4.59 WI-FI, One-day Wi-Fi access, $1.00 GYM-PEP, Per entry pass to gym (Level 4-01), $1.00 FRIDGE, Mini Fridge (50L), $4.59 WI-FI, One-day Wi-Fi access, $1.00 GYM-PEP, Per entry pass to gym (Level 4-01), $1.00
预期输出
GYM-PEP, Per entry pass to gym (Level 4-01), $1.00 FRIDGE, Mini Fridge (50L), $4.59 WI-FI, One-day Wi-Fi access, $1.00 Duplicate found GYM-PEP, Per entry pass to gym (Level 4-01), $1.00 FRIDGE, Mini Fridge (50L), $4.59 WI-FI, One-day Wi-Fi access, $1.00
问题原因
Python中,in运算符判断对象是否存在于列表时,会调用对象的__eq__方法进行相等性比较。自定义类默认的__eq__方法是比较对象的内存地址(即判断是否为同一个实例),而非比较对象的属性值。
room2Amenity1和room2Amenity4虽然属性完全一致,但它们是两个独立的实例,内存地址不同,因此newItem in self._amenities会返回False,导致重复实例被添加。
解决方案
给Amenity类重写__eq__方法,基于对象的属性值来判断相等性。同时,为了遵循Python的最佳实践,建议一并重写__hash__方法(当对象需要存入集合或作为字典键时,hash值需与__eq__逻辑保持一致)。
修改后的代码
class Amenity(): def __init__(self, itemCode, description, price): self._itemCode = itemCode self._description = description self._price = price def __str__(self): return "{}, {}, ${:.2f}".format(self._itemCode, self._description, self._price) def __eq__(self, other): # 先判断是否为Amenity类型 if not isinstance(other, Amenity): return False # 比较所有关键属性 return (self._itemCode == other._itemCode and self._description == other._description and self._price == other._price) def __hash__(self): # 基于参与相等性比较的属性生成hash值 return hash((self._itemCode, self._description, self._price)) class Room(): def __init__(self): self._amenities = [] def addAmenity(self, newItem): if newItem in self._amenities: return "Duplicate found" else: self._amenities.append(newItem) def __str__(self): amenityListing = "\n".join(str(amenity) for amenity in self._amenities) return amenityListing def main(): room2 = Room() room2Amenity1 = Amenity("GYM-PEP","Per entry pass to gym (Level 4-01)",1.00) room2Amenity2 = Amenity("FRIDGE","Mini Fridge (50L)",4.59) room2Amenity3 = Amenity("WI-FI","One-day Wi-Fi access",1.00) room2Amenity4 = Amenity("GYM-PEP","Per entry pass to gym (Level 4-01)",1.00) room2.addAmenity(room2Amenity1) room2.addAmenity(room2Amenity2) room2.addAmenity(room2Amenity3) print(room2) print() result = room2.addAmenity(room2Amenity4) print(result) print() print(room2)
修改后输出
GYM-PEP, Per entry pass to gym (Level 4-01), $1.00 FRIDGE, Mini Fridge (50L), $4.59 WI-FI, One-day Wi-Fi access, $1.00 Duplicate found GYM-PEP, Per entry pass to gym (Level 4-01), $1.00 FRIDGE, Mini Fridge (50L), $4.59 WI-FI, One-day Wi-Fi access, $1.00
内容的提问来源于stack exchange,提问作者Long Feng Ho
相关产品推荐
相关产品推荐

