在Python中删除列表的最后一个元素失败

4
我正在尝试在Python中删除列表的最后一个元素:
di = {"a": 3, "children": [{"b": 5}, {"c": 6}]}
for el in di['children']:
  di['children'].remove(el)

我期望的是:
print di
{'a': 3, 'children: []}

但是我得到的是什么?
print di
{'a': 3, 'children': [{'c': 6}]}

有人知道是什么出了问题吗?


5
如果您想删除所有元素,di['children'] = [] 有什么问题吗? - georg
7个回答

11

正如其他人所解释的那样,在迭代列表时无法修改它。

你可以在迭代一个副本的过程中修改列表,但最好还是生成一个新的过滤后的列表:

di = {"a": 3, "children": [{"b": 5}, {"c": 6}]}
di['children'] = [el for el in di['children'] if el not in di['children']]
为什么这样做更好呢?这意味着您避免了改变列表,使得您的代码更容易理解、更容易跟踪、通常更容易编写,并且通常更快、更节省空间。您不必担心在迭代时发生突变的问题,这正是“更容易理解”的一个完美例子。
在某些情况下,这确实可能更难编写、更慢或者不如使用变异器高效,这就是为什么这只是一个指导方针而不是硬性规定的原因。但至少值得思考一下,“我能否将其重写为不可变过滤器而不是变异器”,即使有时答案可能是“不行”。
另外,真的,你的算法保证等同于完全清空吗?如果是这样的话:
di = {"a": 3, "children": [{"b": 5}, {"c": 6}]}
di['children'] = []

抱歉,但那真的让我很困扰。解释得很好。 - mayhewr

6
您不应在迭代列表时修改它。相反,您应该在副本上进行迭代。请参阅Python文档
尝试这样做...
di = {"a": 3, "children": [{"b": 5}, {"c": 6}]}
for el in di['children'][:]:
    di['children'].remove(el)

1
为什么不用一个空列表来替换呢?你现在是先复制一份再移除每个元素... 这种代码设计相当糟糕。 - JBernardo
1
@JBernardo 我猜他简化了代码,否则我完全同意。 - mayhewr
是的,我正在将它应用于更大的对象。感谢大家,知道了解决方案,错误显而易见 :) - Martin Nigsch
在我知道的大多数语言中,当您在迭代集合时进行修改是一个不好的想法。 - hughdbrown
你可以在迭代列表时修改它(但最好不要这样做)。 - Andy Hayden

6

您正在迭代修改一个列表 - 当您删除第一个条目时,第二个条目成为第一个,到达了列表的末尾。相反,请使用:

del di["children"][:]

这将保留原始列表(不像di["children"] = []),因此如果您有其他对它们的引用,它们也将反映截断。


4

只有在向后遍历列表时才能删除循环中的元素。

因此,我认为只需像这样在reversed()迭代器中包装di ['children']即可:

for el in reversed(di['children']):

这是因为删除元素会导致元素编号的更改,所有后续元素的编号都会减少1。但是,如果你向后移动,则不必关心后续元素的索引,只需关注删除之前的元素即可。


3
因为这实际上在迭代列表时成功地进行了修改,而没有复制进行迭代,至少在Python 2.4+中是如此。虽然通常不是一个好主意,但当必要时,知道这是可能的还是值得的。+1 - abarnert

2

您正在迭代修改列表,这是一个不好的做法。

尝试在副本上进行迭代,同时从原始列表中删除元素。


我建议从旧列表中创建一个新列表,过滤出仅包含您想要的元素。 - hughdbrown

2
del di['children'][1]

删除列表中的最后一个元素。


0
 for i in di:
    if type(di[i])==list:
       di[i]=[]

3
这不是类型检查的正确方式,而且上面已经给出了更好的答案。 - SilentGhost
1
此外,它给出了完全错误的答案。它将di中每个list替换为一个空列表,而OP试图删除dict di中特定list的每个元素。 - abarnert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接