Python: 在迭代时向 list 添加元素

我知道在迭代列表时不允许删除元素,但是在迭代时允许向 python 列表添加元素。这里有一个例子:

    for a in myarr:
if somecond(a):
myarr.append(newObj())

我已经在我的代码中尝试了这个,它似乎工作得很好,但是我不知道这是否是因为我只是幸运,它将在未来的某个时刻中断?

编辑: 我不喜欢复制的名单,因为“ myarr”是巨大的,因此它会太慢。我还需要用“ somsecond ()”检查附加的对象。

编辑: 在某一点上,“ some second (a)”将为 false,因此不可能存在无限循环。

编辑: 有人问到“ some second ()”函数。Myarr 中的每个对象都有一个大小,每当“ some second (a)”为真,并且一个新对象被添加到列表中时,新对象的大小将小于 a。“ some second ()”有一个小对象的 ε 表示小对象的大小,如果它们太小,它将返回“ false”

143003 次浏览

复制你的原始列表,迭代它, 请参阅下面的修改代码

for a in myarr[:]:
if somecond(a):
myarr.append(newObj())

根据 http://docs.python.org/tutorial/controlflow.html

修改序列是不安全的 在循环中迭代(这个 只能发生在可变序列上 类型,例如列表) 修改正在迭代的列表 (例如,复制选定的 条目) ,您必须遍历一个副本。

通过 i 直接访问列表元素,然后可以在列表中添加:

for i in xrange(len(myarr)):
if somecond(a[i]):
myarr.append(newObj())

你能做到的。

bonus_rows = []
for a in myarr:
if somecond(a):
bonus_rows.append(newObj())
myarr.extend( bonus_rows )

您可以使用 itertools 中的 islice在列表的一小部分上创建迭代器。然后,您可以将条目附加到列表中,而不会影响您正在迭代的条目:

islice(myarr, 0, len(myarr)-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

你为什么不用惯用的 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

我今天也遇到了类似的问题。我有一个需要检查的项目列表; 如果对象通过了检查,它们将被添加到结果列表中。如果它们通过了 没有,我会对它们进行一些更改,如果它们仍然有效(更改后大小 > 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个项目) ,这个方法也很有效。

如果希望循环也循环遍历在循环过程中添加到列表中的元素,可以使用 index 和 while 循环代替 for 循环:

i = 0
while i < len(myarr):
a = myarr[i];
i = i + 1;
if somecond(a):
myarr.append(newObj())

替代方案:

reduce(lambda x,newObj : x +[newObj] if somecond else x,myarr,myarr)

简而言之 : 如果您完全确定所有新对象都无法通过 somecond()检查,那么您的代码就可以正常工作,只是会浪费一些时间来迭代新添加的对象。

在给出一个正确的答案之前,您必须理解为什么在迭代时更改 list/dict 会被认为是一个坏主意。使用 for语句时,Python尝试成为 聪明,并每次返回一个动态计算的项。以 list为例,python记住一个索引,并且每次返回 l[index]给你。如果你改变 l,结果 l[index]可能是混乱的。

注意 : 这里有一个 堆栈溢出问题来演示这一点。

在迭代时添加元素的最坏情况是 无限循环,尝试(或者不尝试,如果您可以读取一个 bug)在 python REPL 中执行以下操作:

import random


l = [0]
for item in l:
l.append(random.randint(1, 1000))
print item

它将不停地打印数字,直到内存耗尽,或被系统/用户杀死。

了解内部原因,让我们讨论一下解决方案:

1. 复印产地来源清单

迭代原始列表,并修改复制的列表。

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)

假设你在这个列表的最后添加了 arr,你可以试试我经常使用的这个方法,

arr = [...The list I want to work with]
current_length = len(arr)
i = 0
while i < current_length:
current_element = arr[i]
do_something(arr[i])
# Time to insert
insert_count = 1 # How many Items you are adding add the last
arr.append(item_to_be inserted)
# IMPORTANT!!!!  increase the current limit and indexer
i += 1
current_length += insert_count

这只是一个样板,如果你运行它,你的程序会因为无限循环而冻结。不要忘记终止循环,除非你需要这样做。