You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

游戏运行时与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)改变,同时你用武器对象本身作为字典的键来统计数量:

  1. 初始游戏中,添加的short_sword都是同一个实例(来自sale_items_dict的引用),所以self.pack里的多个同类型武器是同一个对象,字典会将它们归为同一个键。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 19:18:38