游戏运行时与pickle加载后背包武器排序及统计异常问题
问题描述
我开发地牢爬行游戏已有数月,玩家背包self.pack存储获取的武器对象,通过字典选择物品追加。运行时inventory函数按damage_bonus排序并统计数量显示正常;但用pickle保存并加载玩家对象后,追加short_sword实例会出现同一武器重复统计的异常,且物品增删后问题更严重。
铁匠售卖函数代码
def blacksmith_sale(self): sale_items_dict = {'1': short_sword, '2': broad_sword, '3': quantum_sword } ... sale_item_key = input("Enter item number to buy, your pack (I)nventory, or (E)xit the blacksmith:").lower() ... sale_item = (sale_items_dict[sale_item_key]) if self.gold >= sale_item.sell_price and self.level >= sale_item.minimum_level: print(f"You buy a {sale_item}") self.gold -= sale_item.buy_price self.pack.append(sale_item) time.sleep(1) self.hud() continue
背包显示函数代码
def inventory(self): while True: if len(self.pack): self.hud() print("Your pack contains:") print("Item Quantity") print() self.pack.sort(key=lambda x: x.damage_bonus) # 按damage_bonus排序武器 stuff_dict = {} for item in self.pack: stuff_dict[item] = self.pack.count(item) for key, value in stuff_dict.items(): print(key, 's', ': ', value, sep='') # 添加复数s并显示数量 print() else: print("Your pack is empty") ...
运行效果对比
- 正常运行时显示:
Your pack contains: Item Quantity Short Swords: 1 Broad Swords: 1
- pickle加载后追加物品显示:
Your pack contains: Item Quantity Short Swords: 1 Short Swords: 1 Broad Swords: 1
问题原因
核心问题出在pickle序列化/反序列化后,对象的身份(id)改变,同时你用武器对象本身作为字典的键来统计数量:
- 初始游戏中,添加的
short_sword都是同一个实例(来自sale_items_dict的引用),所以self.pack里的多个同类型武器是同一个对象,字典会将它们归为同一个键。 - pickle保存再加载后,玩家对象及背包内的武器会被重建为新实例。此时,加载前的武器和加载后新添加的同类型武器(来自当前
sale_items_dict的实例)是两个不同对象,即使属性完全一致,id不同会被字典判定为不同键,导致重复显示。
另外,self.pack.count(item)效率极低,每次遍历整个列表,物品越多性能越差,还会加剧统计异常的表现。
解决方案
方案1:让武器对象基于属性判定相等性
修改武器类,重写__eq__和__hash__方法,让属性相同的武器被视为同一个对象:
class Weapon: def __init__(self, name, damage_bonus, sell_price, buy_price, minimum_level): self.name = name self.damage_bonus = damage_bonus self.sell_price = sell_price self.buy_price = buy_price self.minimum_level = minimum_level def __eq__(self, other): if not isinstance(other, Weapon): return False # 比较核心属性,判定是否为同一种武器 return (self.name == other.name and self.damage_bonus == other.damage_bonus and self.sell_price == other.sell_price and self.buy_price == other.buy_price and self.minimum_level == other.minimum_level) def __hash__(self): # 基于核心属性生成哈希值 return hash((self.name, self.damage_bonus, self.sell_price, self.buy_price, self.minimum_level)) def __str__(self): return self.name # 确保打印时显示正确名称
这样即使pickle加载后的对象是新实例,只要属性一致,就会被字典视为同一个键,统计数量正常。
方案2:用武器的唯一标识作为统计键
修改inventory函数,不用对象本身当键,改用武器名称(或其他唯一属性):
def inventory(self): while True: if len(self.pack): self.hud() print("Your pack contains:") print("Item Quantity") print() self.pack.sort(key=lambda x: x.damage_bonus) stuff_dict = {} for item in self.pack: # 用武器名称作为统计键 key = item.name stuff_dict[key] = stuff_dict.get(key, 0) + 1 for key, value in stuff_dict.items(): # 根据数量自动处理复数形式 item_str = f"{key}s" if value > 1 else key print(f"{item_str}: {value}") print() else: print("Your pack is empty") ...
该方案更简单,直接绕开对象身份问题,且stuff_dict.get(key, 0) + 1只需遍历一次列表,效率远高于原代码的self.pack.count(item)。
额外优化:改用字典存储背包
如果武器支持堆叠,建议直接用字典存储(键为武器对象/标识,值为数量),序列化后无异常,增删统计操作更高效:
# 初始化背包 self.pack = {} # 购买物品时更新背包 if sale_item in self.pack: self.pack[sale_item] += 1 else: self.pack[sale_item] = 1 # 显示背包时直接遍历字典
内容的提问来源于stack exchange,提问作者misfit138
相关产品推荐
相关产品推荐

