Python:迭代时将元素添加到列表中

时间:2010-09-20 14:54:24

标签: python iteration

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

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

我已经在我的代码中尝试了这个并且似乎工作正常,但是我不知道是不是因为我很幸运并且它将来会在某个时候中断?

编辑:我不喜欢复制列表,因为“myarr”很大,因此它太慢了。另外,我需要用“somecond()”检查附加的对象。

编辑:在某些时候“somecond(a)”将为false,因此不会有无限循环。

编辑:有人问过“somecond()”函数。 myarr中的每个对象都有一个大小,每次“somecond(a)”为真,并且一个新对象被附加到列表中,新对象的大小将小于a。 “somecond()”有一个epsilon表示小对象可以如何,如果它们太小则会返回“false”

11 个答案:

答案 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

它将不间断地打印数字,直到内存用完或被系统/用户终止。

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

1。制作原产地清单

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

result = l[:]
for item in l:
    if somecond(item):
        result.append(Obj())

2。控制何时循环结束

不是处理对python的控制,而是决定如何迭代列表:

length = len(l)
for index in range(length):
    if somecond(l[index]):
        l.append(Obj())

在迭代之前,计算列表长度,并且只循环length次。

3。将添加的对象存储在新列表中

不是修改原始列表,而是将新对象存储在新列表中,然后将它们连接起来。

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)