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

Wes*_*Dec 51 python iteration

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

    for a in myarr:
      if somecond(a):
          myarr.append(newObj())
Run Code Online (Sandbox Code Playgroud)

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

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

编辑:在某些时候"somecond(a)"将是假的,因此不会有无限循环.

编辑:有人问过"somecond()"函数.myarr中的每个对象都有一个大小,每次"somecond(a)"为真,并且一个新对象被附加到列表中,新对象的大小将小于a."somecond()"有一个关于小物体如何的epsilon,如果它们太小,它将返回"false"

小智 49

你为什么不用惯用的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  
Run Code Online (Sandbox Code Playgroud)

如果您只想迭代最初在列表中的元素:

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
Run Code Online (Sandbox Code Playgroud)

  • Python的列表就像C数组或C++向量; 索引它们是恒定时间.这实际上是一个非常好的解决方案,因为它可以执行OP的算法,而不依赖于未定义的行为. (12认同)

Roh*_*nga 20

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

修改循环中迭代的序列是不安全的(这只能发生在可变序列类型中,例如列表).如果您需要修改正在迭代的列表(例如,复制所选项目),则必须迭代副本.

  • 文档似乎已经改变,现在它并没有说它不安全,只是它_可能很棘手_:**在迭代同一集合时修改集合的代码可能很难正确处理。相反,循环遍历集合的副本或创建新集合通常更直接:** (6认同)
  • 如何使用索引而不是使用“ for myarr”遍历列表呢?即`i = 0; 而我&lt;len(myarr):a = myarr [i]; 我=我+ 1; 如果somecond(a):myarr.append(newObj())` (2认同)

whe*_*ies 7

You could use the islice from itertools to create an iterator over a smaller portion of the list. Then you can append entries to the list without impacting the items you're iterating over:

islice( myarr, 0, len(myarr)-1 )
Run Code Online (Sandbox Code Playgroud)

Even better, you don't even have to iterate over all the elements. You can increment a step size.

  • 抱歉,我错了,你现在得到了赞成票:)(注意:我必须编辑你的答案才能更改我的投票)一个好的测试确实如另一个答案中所建议的那样:`a = [0] \nfor i in a:\n print(a)\n if i &lt; 100:\n a.append(i+1)`。这正确地打印了从 0 到 100 的所有整数。这证明 python 列表迭代器实际上并不直接指向内存:它们存储指向列表对象的指针和索引。 (2认同)

S.L*_*ott 6

You can do this.

bonus_rows = []
for a in myarr:
  if somecond(a):
      bonus_rows.append(newObj())
myarr.extend( bonus_rows )
Run Code Online (Sandbox Code Playgroud)

  • @WesDec:或者停止使用简单的列表并使用树。这听起来像是广度优先搜索,其中列表是错误的结构。 (2认同)

ciz*_*ixs 6

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

在给出正确的答案之前,您必须了解为什么在迭代时更改列表/命令是一个坏主意。使用forstatement时,Python请尽量聪明一些,并每次返回一个动态计算的项目。以list为例,python记得有一个指标,每一次返回l[index]给你。如果要更改l,结果l[index]可能会很混乱。

注意:这是一个stackoverflow问题,以证明这一点。

在迭代时添加元素的最坏情况是无限循环,请在python REPL中尝试以下操作(如果不能读懂错误,请尝试以下操作):

import random

l = [0]
for item in l:
    l.append(random.randint(1, 1000))
    print item
Run Code Online (Sandbox Code Playgroud)

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

了解内部原因,让我们讨论解决方案。这里有一些:

1.复制原产地清单

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

result = l[:]
for item in l:
    if somecond(item):
        result.append(Obj())
Run Code Online (Sandbox Code Playgroud)

2.控制循环何时结束

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

length = len(l)
for index in range(length):
    if somecond(l[index]):
        l.append(Obj())
Run Code Online (Sandbox Code Playgroud)

迭代之前,请计算列表长度,仅计算循环length时间。

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

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

added = [Obj() for item in l if somecond(item)]
l.extend(added)
Run Code Online (Sandbox Code Playgroud)