Python Counter统计自定义类实例库存打印异常问题
问题原因
collections.Counter 统计重复元素的判断规则是:两个元素哈希值一致、且等值判定为True时,才会被归为同一项累计数量。
- 库存存字符串时,值相同的字符串天然哈希一致、等值判定成立,因此Counter可以正常合并统计。
- 换成自定义类实例后,每次调用
Log()、Flint()这类构造方法生成的都是独立新实例。Python自定义类默认的等值判定规则是「只有实例自身和自身相等」,不同实例哪怕由同一个类创建、属性完全相同,也会被判定为不同对象,哈希值也互不相同,因此Counter无法自动合并同类型物品。
修复方案
可根据代码结构选择以下任意一种方案:
方案1:补全物品基类的等值和哈希方法(推荐)
从根源上让同类型物品实例满足Counter的统计要求,后续其他逻辑判断两个物品是否为同一种时也能复用规则。
仅需修改Item基类,补充__eq__和__hash__两个魔法方法,所有子类无需改动:
class Item: def __init__(self, name, description): self.name = name self.description = description def __str__(self): return self.name.title() def __eq__(self, other): if not isinstance(other, Item): return False # 同类型且名称一致,判定为同一种物品 return type(self) is type(other) and self.name == other.name def __hash__(self): # 基于物品类型和名称生成哈希,保证同一种物品哈希一致 return hash((type(self), self.name)) # 以下Log、Flint、SteelScrap子类代码保持原有逻辑不变即可 class Log(Item): def __init__(self): super().__init__(name="Log", description="A Large wooden log.") class Flint(Item): def __init__(self): super().__init__(name="Flint", description="A fragment of flint.") class SteelScrap(Item): def __init__(self): super().__init__(name="Steel Scrap", description="A piece of steel scrap.")
修改完成后原有Player类的代码不需要任何调整,Counter即可正常统计同类型物品数量。
如果后续给物品增加耐久、附魔等需要区分堆叠的属性,只要把对应属性加入
__eq__的判断条件、以及__hash__的生成元组即可。
方案2:调整计数逻辑,按物品标识手动统计
如果不想修改原有物品类的定义,可以直接调整库存打印的计数逻辑,不直接将实例列表传入Counter,而是手动以物品唯一标识(名称/类型)为key统计:
from collections import Counter from items import * class Player: def __init__(self): self.inventory = [Log(),Log(),Flint(),SteelScrap()] def print_inv(self): print(" -- Inventory -- ") inv_count = Counter() display_map = {} for item in self.inventory: inv_count[item.name] += 1 display_map[item.name] = item for item_name, quantity in inv_count.items(): print(f" > {display_map[item_name]}({quantity})") Player().print_inv()
内容的提问来源于stack exchange,提问作者bigboibob
相关产品推荐
相关产品推荐

