如何在for循环期间修改列表条目?

Ale*_*lex 150 python

现在我知道在迭代循环期间修改列表是不安全的.但是,假设我有一个字符串列表,我想自己去除字符串.替换可变值是否算作修改?

mar*_*eau 142

由于下面的循环只修改已经看过的元素,因此可以认为是可接受的:

a = ['a',' b', 'c ', ' d ']

for i, s in enumerate(a):
    a[i] = s.strip()

print(a) # -> ['a', 'b', 'c', 'd']
Run Code Online (Sandbox Code Playgroud)

这与以下内容不同:

a[:] = [s.strip() for s in a]
Run Code Online (Sandbox Code Playgroud)

因为它不需要创建临时列表并指定它来替换原始列表,尽管它确实需要更多的索引操作.

警告:虽然您可以通过这种方式修改条目,但您无法更改其中的项目数,list而不会有遇到问题的风险.

这是我的意思的一个例子 - 从这一点删除一个条目搞砸索引:

b = ['a', ' b', 'c ', ' d ']

for i, s in enumerate(b):
    if s.strip() != b[i]:  # leading or trailing whitespace?
        del b[i]

print(b)  # -> ['a', 'c ']  # WRONG!
Run Code Online (Sandbox Code Playgroud)

(结果是错误的,因为它没有删除它应该拥有的所有项目.)

  • @Navin:因为`a [i] = s.strip()`只进行一次索引操作. (3认同)
  • 为什么Python只复制语法`for i in a`中的单个元素?这非常违反直觉,看似与其他语言不同,导致我的代码出错,我必须长时间调试.Python教程甚至没有提到它.虽然必须有一些理由吗? (2认同)
  • @JIXIang:它不会复制。它只是将循环变量名称分配给正在迭代的事物的连续元素或值。 (2认同)
  • @Navin:使用`enumerate()` 不会添加索引操作。但是,无论是否执行,每次迭代执行的总次数明显少于通过 `a[i] = s.strip()` 执行的 `a[i] = a[i].strip()` . (2认同)
  • @variable:不知道这个概念的具体名称。该问题与列表如何在内部存储和迭代有关(没有记录,并且在不同版本中可能有所不同)。对我来说,如果正在迭代的内容在迭代时发生更改,则操作可能会“混乱”(即无法正确完成),这似乎非常合乎逻辑。它还取决于修改的内容以及列表中元素的类型。有关详细信息,请参阅[迭代时修改列表](/sf/ask/3140507541/)。 (2认同)

Ign*_*ams 122

它被认为是糟糕的形式.如果需要保留对列表的现有引用,请使用列表推导,使用切片分配.

a = [1, 3, 5]
b = a
a[:] = [x + 2 for x in a]
print(b)
Run Code Online (Sandbox Code Playgroud)

  • 为什么我们指定b = a? (9认同)
  • 为什么一个[:] =而不只是一个=? (9认同)
  • 切片分配很聪明,避免在循环期间修改原始内容,但需要创建原始长度的临时列表. (8认同)
  • @Vigrond:所以当执行`print b`语句时,你可以判断`a`是否被就地修改而不是被替换.另一种可能性是`print b is a',看它们是否仍然引用同一个对象. (8认同)
  • @kdubs:"...如果您需要保留对列表的现有引用,则使用切片分配." (8认同)

小智 18

还有一个用于循环变体,看起来比使用enumerate()更清晰:

for idx in range(len(list)):
    list[idx]=... # set a new value
    # some other code which doesn't let you use a list comprehension
Run Code Online (Sandbox Code Playgroud)

  • 许多人考虑在Python中使用类似`range(len(list))的代码气味. (16认同)
  • @Reishin:由于这个原因你的比较无效.它正在孤立地测量循环开销.要确定必须测量整个循环执行所花费的时间,因为可能会通过以某种方式循环循环中的代码所带来的好处来缓解任何开销差异 - 否则您不会将苹果与苹果. (3认同)
  • @Reishin:因为`enumerate`是一个生成器,所以它不会创建一个元组列表,它在遍历该列表时一次创建一个元组。判断哪个较慢的唯一方法是使用“ timeit”。 (2认同)
  • @martineau [code](http://pastebin.com/p42m0PRK)可能不太漂亮,但是根据`timeit`的说法,enumerate`较慢 (2认同)
  • @Reishin:您的基准测试代码并不完全有效,因为它没有考虑在给定索引处检索列表中的值的需要 - 这也未在此答案中显示. (2认同)

ciz*_*ixs 10

只要不更改要添加/删除的元素到列表,在迭代列表时修改每个元素就可以了.

您可以使用列表理解:

l = ['a', ' list', 'of ', ' string ']
l = [item.strip() for item in l]
Run Code Online (Sandbox Code Playgroud)

或者只是做C-stylefor循环:

for index, item in enumerate(l):
    l[index] = item.strip()
Run Code Online (Sandbox Code Playgroud)


小智 5

Ignacio Vazquez-Abrams 给出的答案非常好。可以通过这个例子进一步说明。设想:

  1. 给你一个包含两个向量的列表。
  2. 您想遍历列表并颠倒每个数组的顺序。

假设你有:

v = np.array([1,2,3,4])
b = np.array([3,4,6])

for i in [v, b]:
    i = i[::-1]   # This command does not reverse the string.

print([v,b])
Run Code Online (Sandbox Code Playgroud)

你会得到:

[array([1, 2, 3, 4]), array([3, 4, 6])]
Run Code Online (Sandbox Code Playgroud)

另一方面,如果你这样做:

v = np.array([1,2,3,4])
b = np.array([3,4,6])

for i in [v, b]:
   i[:] = i[::-1]   # This command reverses the string.

print([v,b])
Run Code Online (Sandbox Code Playgroud)

结果是:

[array([4, 3, 2, 1]), array([6, 4, 3])]
Run Code Online (Sandbox Code Playgroud)