如何让list_odds随Champ对象的odds属性自动同步?
问题描述
我需要用random.choices(list_champs, weights=list_odds)根据概率选择Champion对象,因此要维护Champion对象列表list_champs和对应的概率列表list_odds。但修改list_champs中Champ对象的odds属性后,list_odds无法自动同步更新。
核心需求:
- 频繁执行选择操作,性能优先,不想每次更新属性都重新生成整个
list_odds - 不想手动同步对象属性与
list_odds,希望list_odds能像dict.keys()的视图结构一样,自动跟随对象的odds属性变化
示例代码及运行结果如下:
import random class Champ: def __init__(self, odds): self.odds = odds def __repr__(self): return f"Champ({self.odds=})" # 创建两个Champ实例 champ1 = Champ(odds = 0.25) champ2 = Champ(odds = 0.5) # 生成对象列表和对应的权重列表 list_champs = [champ1, champ2] list_odds = [champ.odds for champ in list_champs] print(f"{list_champs=}") print(f"{list_odds=}") # 执行随机选择操作 random_champ = random.choices(list_champs, weights=list_odds)[0] # 修改champ1的概率值 print("Changing odds : champ1.odds = 0.99") champ1.odds = 0.99 # 期望list_odds能同步更新,但实际并未变化 print(f"{list_champs=}") print(f"{list_odds=}") # 此时随机选择的权重还是旧值 random_champ = random.choices(list_champs, weights=list_odds)[0]
运行输出:
list_champs=[Champ(self.odds=0.25), Champ(self.odds=0.5)] list_odds=[0.25, 0.5] Changing odds : champ1.odds = 0.99 list_champs=[Champ(self.odds=0.99), Champ(self.odds=0.5)] list_odds=[0.25, 0.5]
解决方案
可以自定义一个权重视图类,它不存储固定的权重值,而是在需要时动态从对应的Champ对象中读取odds属性。这样既不用手动同步,也能保证每次使用时都是最新的权重,同时避免了重复生成列表的性能开销。
实现代码
import random class Champ: def __init__(self, odds): self.odds = odds def __repr__(self): return f"Champ({self.odds=})" class WeightsView: def __init__(self, champs_list): self.champs = champs_list # 实现__getitem__,让random.choices能正常访问元素 def __getitem__(self, index): return self.champs[index].odds # 实现__len__,满足random.choices对权重序列的长度要求 def __len__(self): return len(self.champs) # 使用示例 champ1 = Champ(odds=0.25) champ2 = Champ(odds=0.5) list_champs = [champ1, champ2] # 创建权重视图,而非固定列表 list_odds = WeightsView(list_champs) print(f"{list_champs=}") # 打印视图时可以手动转换为列表查看当前权重 print(f"当前权重: {[w for w in list_odds]}") # 执行随机选择 random_champ = random.choices(list_champs, weights=list_odds)[0] # 修改champ1的概率 print("Changing odds : champ1.odds = 0.99") champ1.odds = 0.99 print(f"{list_champs=}") # 再次查看权重,已经同步更新 print(f"当前权重: {[w for w in list_odds]}") # 此时随机选择会使用新的权重 random_champ = random.choices(list_champs, weights=list_odds)[0]
运行结果
list_champs=[Champ(self.odds=0.25), Champ(self.odds=0.5)] 当前权重: [0.25, 0.5] Changing odds : champ1.odds = 0.99 list_champs=[Champ(self.odds=0.99), Champ(self.odds=0.5)] 当前权重: [0.99, 0.5]
原理说明
WeightsView类仅持有Champ对象列表的引用,不存储固定权重值- 当
random.choices访问权重序列元素时,__getitem__方法实时从对应Champ对象读取最新的odds属性 - 实现
__len__方法是为了满足random.choices对权重序列的长度校验要求 - 这种方式性能开销极低,仅在实际需要权重值时才读取属性,避免了预先生成和更新列表的额外成本
内容的提问来源于stack exchange,提问作者Ernest Klimann
相关产品推荐
相关产品推荐

