【问题标题】:Why does appending to a dict alter the first list?为什么附加到 dict 会改变第一个列表?
【发布时间】:2021-03-10 14:13:56
【问题描述】:

我正在尝试创建一个字典,其中每个键都是试用标识名称,关联的值是一个列表,其中包含许多元素作为整数,即 {'a':[1,3,4,2,1], 'b':[34,5,3,23]}。我想我会从一些虚拟数据开始,当我使用 for 循环和 append() 时,它会直接更改我的原始数据。我已经确定当我尝试添加到我的 dict 时会发生这种情况,但不明白为什么。

我的虚拟值和代码如下:

plane1 = [1,1,1]
plane2 = [2,2,2]
plane3 = [3,3,3]
plane4 = [4,4,4]
zList = [plane1,plane2,plane3,plane4]
trial_list = ['a', 'b', 'c']

roi_dict = {}

for trial in trial_list:
    print(trial)
    for z in zList:
        if trial not in roi_dict.keys():
            roi_dict[str(trial)] = z
        else:
            roi_dict[str(trial)].append(z)

最终的 roi_dict 是

{'a': [1, 1, 1, [2, 2, 2], [3, 3, 3], [4, 4, 4], [2, 2, 2], [3, 3, 3], [4, 4, 4], [2, 2, 2], [3, 3, 3], [4, 4, 4]], 'b': [1, 1, 1, [2, 2, 2], [3, 3, 3], [4, 4, 4], [2, 2, 2], [3, 3, 3], [4, 4, 4], [2, 2, 2], [3, 3, 3], [4, 4, 4]], 'c': [1, 1, 1, [2, 2, 2], [3, 3, 3], [4, 4, 4], [2, 2, 2], [3, 3, 3], [4, 4, 4], [2, 2, 2], [3, 3, 3], [4, 4, 4]]}

print(z_list[0])
[1, 1, 1, [2, 2, 2], [3, 3, 3], [4, 4, 4], [2, 2, 2], [3, 3, 3], [4, 4, 4], [2, 2, 2], [3, 3, 3], [4, 4, 4]]

我不明白为什么 A) 键的前三个元素是单独的数字,而后面的元素是列表。 B) 为什么每个键的值不是 [1, 1, 1], [2, 2, 2], [3, 3, 3], [4, 4, 4]。 C) 为什么这会改变 func_list 的第一个值。

如果我应该把它分成多个问题,我会非常乐意,但我觉得这些都可能源于同一个问题。

谢谢!

【问题讨论】:

  • 你可以试试roi_dict[str(trial)] = [z],但如果不是minimal reproducible example,我投票关闭需要调试细节。
  • 我很抱歉!是的,我一直在努力尝试将其解析,但我意识到我的变量名大多是单个字母,其他任何人都无法理解,所以我将 l1 更改为 plane1 等,并将 func_list 更改为 zList。感谢您指出这一点,我应该更仔细地阅读它。
  • 无论如何,您总是重复使用相同的列表。您在此处更改这些列表.append(z)。这就是他们被改变的原因。它们看起来不对的原因是因为首先您将列表本身添加为值,例如roi_dict[str(trial)] = z 然后你追加到那个列表。所以考虑一下,lst1 = [1,2,3]; lst2 = [4,5,6]; dct = {'a':lst1}; dct['a'].append(lst2)
  • 所以,我不清楚您实际上想要完成什么。你想要的结果是什么?
  • 我希望 roi_dict 返回 {'a': [1, 1, 1], [2, 2, 2], [3, 3, 3], [4, 4, 4] , 'b': [1, 1, 1], [2, 2, 2], [3, 3, 3], [4, 4, 4], 'c': [1, 1, 1], [ 2, 2, 2], [3, 3, 3], [4, 4, 4]},目的是使用它来最终找到每个试验中每个平面中的神经元数量。也感谢您之前的 cmets!

标签: python-3.x list dictionary for-loop


【解决方案1】:

zList 是一个列表列表。你会的

 if trial not in roi_dict.keys():
    roi_dict[str(trial)] = z           # z being intem from zList
else:
    roi_dict[str(trial)].append(z)

意味着您将roi_dict[str(trial)] 设置为z

然后你追加到roi_dict[str(trial)] - 列表被引用使用,所以你基本上roi_dict[str(trial)] == z 并且在进一步迭代中修改roi_dict[str(trial)].append(z) 时修改zList[0]

将您的代码更改为

import copy

 if trial not in roi_dict.keys():
    roi_dict[str(trial)] = [copy.deepcopy(z)]
 else:
    roi_dict[str(trial)].append(z)

解决这两个问题。

【讨论】:

  • @juanpa 如果原始列表确实包含整数,则不需要 deepcopy - 那么您可以使用 roi_dict[str(trial)] = [z[:]] 但它说这是简化的。
【解决方案2】:

A) 因为第一次分配给该键时,您将其设置为列表 [1, 1, 1],这意味着 roi_dict["a"] = [1, 1, 1]。但是对于后续循环,您将一个列表附加到此列表而不是设置到 dict 条目。这意味着在您的第二个循环中,它变为roi_dict["a"] = [1, 1, 1, [2, 2, 2]]

B) 如果你想要这个,你需要设置

roi_dict["a"] = [1, 1, 1]
roi_dict["b"] = [2, 2, 2]

等等。在 for 循环中:

for z, trial in zip(zList, trial_list):
     roi_dict[trial] = z
    

C) 因为python中的一切都是指针。即:当您将列表分配给字典条目时,对该字典条目的任何引用都将指向原始列表。换句话说:

>> l = []
>> d = {"list": l}
>> d["list"].append(0)
>> print(l)
>> # l = [0] now because we accessed it via the dictionary

如果您想避免这种行为,您需要使用复制函数来复制数据,而不是创建引用。

【讨论】:

  • 非常感谢,这非常有用!
猜你喜欢
  • 1970-01-01
  • 2022-01-24
  • 2022-01-08
  • 2012-07-20
  • 2015-09-22
  • 2021-06-07
  • 2016-05-11
  • 2022-11-21
  • 1970-01-01
相关资源
最近更新 更多