我知道在迭代列表时不允许删除元素,但允许在迭代时将元素添加到python列表中。这是一个例子:
for a in myarr:
if somecond(a):
myarr.append(newObj())
我已经在我的代码中尝试了这个并且似乎工作正常,但是我不知道是不是因为我很幸运并且它将来会在某个时候中断?
编辑:我不喜欢复制列表,因为“myarr”很大,因此它太慢了。另外,我需要用“somecond()”检查附加的对象。编辑:在某些时候“somecond(a)”将为false,因此不会有无限循环。
编辑:有人问过“somecond()”函数。 myarr中的每个对象都有一个大小,每次“somecond(a)”为真,并且一个新对象被附加到列表中,新对象的大小将小于a。 “somecond()”有一个epsilon表示小对象可以如何,如果它们太小则会返回“false”答案 0 :(得分:45)
为什么不用惯用的C方式呢?这应该是防弹的,但它不会很快。我很确定索引到Python中的列表会走链表,所以这是一个“Shlemiel the Painter”算法。但是,在明确特定部分代码确实存在问题之前,我倾向于不担心优化。首先让它工作;然后担心如果有必要让它快速。
如果要迭代所有元素:
i = 0
while i < len(some_list):
more_elements = do_something_with(some_list[i])
some_list.extend(more_elements)
i += 1
如果您只想迭代最初在列表中的元素:
i = 0
original_len = len(some_list)
while i < original_len:
more_elements = do_something_with(some_list[i])
some_list.extend(more_elements)
i += 1
答案 1 :(得分:19)
嗯,根据http://docs.python.org/tutorial/controlflow.html
修改序列是不安全的 在循环中迭代(这个 只能发生可变序列 类型,如列表)。如果你需要 修改你正在迭代的列表 (例如,复制选中的 你必须迭代一份副本。
答案 2 :(得分:6)
你可以这样做。
bonus_rows = []
for a in myarr:
if somecond(a):
bonus_rows.append(newObj())
myarr.extend( bonus_rows )
答案 3 :(得分:6)
您可以使用itertools中的islice
在列表的较小部分上创建迭代器。然后,您可以将条目附加到列表中,而不会影响您正在迭代的项目:
islice( myarr, 0, len(myarr)-1 )
更好的是,您甚至不必遍历所有元素。您可以增加步长。
答案 4 :(得分:5)
简而言之:如果您确定所有新对象都无法somecond()
检查,那么您的代码工作正常,只会浪费一些时间来迭代新添加的对象。< / p>
在给出正确的答案之前,你必须理解为什么在迭代时改变列表/字典是一个坏主意。使用for
语句时,Python
会尝试聪明,并且每次都会返回一个动态计算的项目。以list
为例,python
会记住索引,每次都会向您返回l[index]
。如果您要更改l
,结果l[index]
可能会变得混乱。
注意:这是一个stackoverflow question来证明这一点。
迭代时添加元素的最坏情况是无限循环,在python REPL中尝试(或者如果你可以读取错误)以下内容:
import random
l = [0]
for item in l:
l.append(random.randint(1, 1000))
print item
它将不间断地打印数字,直到内存用完或被系统/用户终止。
了解内部原因,让我们讨论解决方案。以下是一些:
迭代原点列表,并修改复制的列表。
result = l[:]
for item in l:
if somecond(item):
result.append(Obj())
不是处理对python的控制,而是决定如何迭代列表:
length = len(l)
for index in range(length):
if somecond(l[index]):
l.append(Obj())
在迭代之前,计算列表长度,并且只循环length
次。
不是修改原始列表,而是将新对象存储在新列表中,然后将它们连接起来。
added = [Obj() for item in l if somecond(item)]
l.extend(added)
答案 5 :(得分:3)
复制原始列表,迭代它, 请参阅下面修改后的代码
for a in myarr[:]:
if somecond(a):
myarr.append(newObj())
答案 6 :(得分:3)
直接通过i访问列表元素。然后你可以附加到你的列表中:
for i in xrange(len(myarr)):
if somecond(a[i]):
myarr.append(newObj())
答案 7 :(得分:2)
我今天遇到了类似的问题。我有一份需要检查的项目清单;如果对象通过了检查,则会将它们添加到结果列表中。如果他们没有通过,我会稍微更改它们,如果它们仍然可以工作(更改后大小> 0),我会将它们添加到列表的后面以进行重新检查。
我选择了像
这样的解决方案items = [...what I want to check...]
result = []
while items:
recheck_items = []
for item in items:
if check(item):
result.append(item)
else:
item = change(item) # Note that this always lowers the integer size(),
# so no danger of an infinite loop
if item.size() > 0:
recheck_items.append(item)
items = recheck_items # Let the loop restart with these, if any
我的列表实际上是一个队列,应该使用某种队列。但是我的名单很小(比如10个项目),这也有效。
答案 8 :(得分:1)
扩展S.Lott的答案,以便处理新项目:
todo = myarr
done = []
while todo:
added = []
for a in todo:
if somecond(a):
added.append(newObj())
done.extend(todo)
todo = added
最终列表位于done
。
答案 9 :(得分:1)
如果您希望循环也循环遍历在循环期间添加到列表中的元素,则可以使用索引和while循环而不是for循环:
i = 0
while i < len(myarr):
a = myarr[i];
i = i + 1;
if somecond(a):
myarr.append(newObj())
答案 10 :(得分:1)
一行中的替代解决方案:
reduce(lambda x,y : x +[newObj] if somecond else x,myarr,myarr)