collections.Counter.most_common 遇三位数时排序异常排查
根本原因
这是字符串字典序排序和数值排序规则错位导致的典型问题,两个环节叠加触发了排序错误:
- 你使用的INI配置解析器读取文件时,默认会把所有值存为字符串格式,而不是整数。也就是说配置里写的
100、35、49在程序里本质是文本字符串,不是可以用来做大小比较的数字。 - 你直接把配置字典传给
Counter做排序时,Counter.most_common()的内部排序逻辑是对传入值做逐字符的字典序比较,和真实的数值大小完全无关。字典序的比较规则是从左到右逐字符对照ASCII值定优先级:"49"的首字符是'4',"35"的首字符是'3',"100"的首字符是'1',按ASCII值从高到低排的话优先级为'4' > '3' > '1',自然就出现了100余额的用户被排到两位数余额用户后面的错位结果。 - 之所以余额是1位或2位数的时候不会出问题,是因为长度在两位及以下的数值,逐位字符串比较的结果和数值大小比较刚好一致;一旦出现三位数,以'1'开头的字符串在字典序下优先级会低于所有首位为2-9的单、两位数,排序错误就会暴露出来。
顺带提一句:这个场景下你本来就是要对每个用户的唯一余额值做排序,根本不存在需要统计元素出现频次的需求,硬套Counter本身就是工具选择错了。
修复方案
只要在读取配置后,显式把字符串格式的余额转成整数,用原生排序逻辑按真实数值大小排序就行,修正后的可运行代码如下:
# 记得把导入语句移到文件顶部全局区,不要写在命令内部 from configparser import ConfigParser # scores = ConfigParser() # 就是你代码里已经定义好的配置解析实例 @bot.command(name='top') async def top(context): scores.read("brewscores.ini") # 取出所有(用户名, 余额)的键值对 score_entries = scores['scores'].items() # 显式把字符串格式的余额转为整数,按数值从高到低排序 sorted_scores = sorted( score_entries, key=lambda entry: int(entry[1]), reverse=True ) # 取前5条生成展示文本 top5_text = "\n".join([f"{username}: {balance}" for username, balance in sorted_scores[:5]]) em = discord.Embed( title="Top 5 Balancers", description=f'The top 5 contestants are!:\n{top5_text}' ) await context.send(embed=em)
- 小优化点:原来写在命令内部的
Counter导入语句可以直接删掉,不需要每次调用指令都重复执行导入操作。
内容的提问来源于stack exchange,提问作者TheTechRobo
相关产品推荐
相关产品推荐

