为什么list.remove()的行为令人意外?
from pprint import *
sites = [['a','b','c'],['d','e','f'],[1,2,3]]
pprint(sites)
for site in sites:
sites.remove(site)
pprint(sites)
输出结果:
[['a', 'b', 'c'], ['d', 'e', 'f'], [1, 2, 3]]
[['d', 'e', 'f']]
为什么它不是 None,或者一个空列表 [] 呢?
3 个回答
3
通常情况下,我会认为迭代器会因为修改了连接的列表而停止。不过在字典的情况下,这种情况至少会发生。
那么,为什么d、e、f这些东西没有被移除呢?我只能猜测:可能迭代器内部有一个计数器(或者它甚至只是基于“备用迭代协议”,也就是用getitem来实现)。
也就是说,第一个返回的项目是sites[0]
,也就是['a', 'b', 'c']
。这个项目随后被从列表中移除。
第二个是sites[1]
,它是[1, 2, 3]
,因为索引已经改变了。这个也被移除了。
第三个应该是sites[2]
,但是由于这会导致索引错误,迭代器就停止了。
12
在Python中,一边遍历一个集合一边改变它的大小,就像在C和C++中做一些不明智的事情一样,可能会导致一些意想不到的错误。你可能会遇到异常,或者行为会变得很奇怪。总之,不要这样做。在这个特定的情况下,背后发生的事情大概是这样的:
- 迭代器从索引0开始,记住自己在索引0,并给你返回那个位置的项目。
- 你把索引0的项目删除了,之后的所有项目都向左移动了一位,以填补这个空缺。
- 当迭代器被要求获取下一个项目时,它会把当前的索引加1,记住新的索引是1,然后给你返回这个位置的项目。但是因为刚才删除项目的操作,索引1现在其实是原本在索引2的项目(也就是最后一个项目)。
- 你把这个项目也删除了。
- 当迭代器被要求获取下一个项目时,它会发现下一个索引(2)已经超出了范围(现在的范围是0到0)。
54
这是因为你在遍历一个列表的时候,同时还在修改它。这样做是不对的。
对于这种情况,你应该先复制一份列表,然后再对复制的那份进行遍历。
for site in sites[:]:
sites.remove(site)