Python循环中追加字典至列表异常的原因及解决方法
问题分析与解决:字典引用导致的列表元素异常变化
你的代码
class Result: def __init__(self): self.limit = 10 self.params_with_error = [] def get_result(self, offset_range): qs = {'limit': self.limit} for chunk in range(offset_range): print(f'chunk: {chunk}') print(f'__Params_with_error__: {self.params_with_error}') qs.update({'offset': chunk * self.limit}) if chunk in (5, 7): print('IF condition') self.params_with_error.append(qs) print(f'Result param: {self.params_with_error}') result = Result() result.get_result(10)
实际输出
chunk: 0 __Params_with_error__: [] chunk: 1 __Params_with_error__: [] chunk: 2 __Params_with_error__: [] chunk: 3 __Params_with_error__: [] chunk: 4 __Params_with_error__: [] chunk: 5 __Params_with_error__: [] IF condition chunk: 6 __Params_with_error__: [{'limit': 10, 'offset': 50}] chunk: 7 __Params_with_error__: [{'limit': 10, 'offset': 60}] IF condition chunk: 8 __Params_with_error__: [{'limit': 10, 'offset': 70}, {'limit': 10, 'offset': 70}] chunk: 9 __Params_with_error__: [{'limit': 10, 'offset': 80}, {'limit': 10, 'offset': 80}] Result param: [{'limit': 10, 'offset': 90}, {'limit': 10, 'offset': 90}]
你的预期输出
[{'limit': 10, 'offset': 50}, {'limit': 10, 'offset': 70}]
为什么会出现这种情况?
核心原因是Python里的字典是可变对象:你在循环外只创建了一个qs字典,之后每次qs.update()都是修改同一个字典对象,而self.params_with_error.append(qs)只是把这个字典的引用添加到列表里,不是复制一份新字典。
直白点说:列表里的两个元素其实指向同一个字典。后续循环中每次修改qs的offset,都会同时影响列表里的这两个引用,直到最后一次循环(chunk=9)把offset改成了90,所以列表里的两个元素最终都显示90。
怎么得到预期结果?
有两种简单有效的解决方法:
方法1:每次append时复制字典副本
把self.params_with_error.append(qs)改成self.params_with_error.append(qs.copy()),这样每次添加的都是当前qs的独立副本,后续修改原qs不会影响已经存入列表的内容。修改后的代码片段:
if chunk in (5, 7): print('IF condition') self.params_with_error.append(qs.copy())方法2:把字典定义放到循环内部
每次循环都创建一个新的qs字典,这样每次append的都是完全独立的字典对象:def get_result(self, offset_range): for chunk in range(offset_range): print(f'chunk: {chunk}') print(f'__Params_with_error__: {self.params_with_error}') # 每次循环新建字典 qs = {'limit': self.limit, 'offset': chunk * self.limit} if chunk in (5, 7): print('IF condition') self.params_with_error.append(qs) print(f'Result param: {self.params_with_error}')
两种方法都能得到你预期的输出结果。
内容的提问来源于stack exchange,提问作者Jekson
相关产品推荐
相关产品推荐

