改变你正在迭代的序列

for 循环遍历序列,因此在循环内更改此序列可能会导致意外结果 (特别是在添加或删除元素时):

alist = [0, 1, 2]
for index, value in enumerate(alist):
    alist.pop(index)
print(alist)
# Out: [1]

注意:list.pop() 用于从列表中删除元素。

第二个元素未被删除,因为迭代按顺序遍历索引。上面的循环迭代两次,结果如下:

# Iteration #1
index = 0
alist = [0, 1, 2]
alist.pop(0) # removes '0'

# Iteration #2
index = 1
alist = [1, 2]
alist.pop(1) # removes '2'

# loop terminates, but alist is not empty:
alist = [1]

出现这个问题是因为索引在向增加索引的方向上迭代时发生变化。要避免此问题,你可以向后遍历循环

alist = [1,2,3,4,5,6,7]
for index, item in reversed(list(enumerate(alist))):
    # delete all even items
    if item % 2 == 0:
        alist.pop(index)
print(alist)
# Out: [1, 3, 5, 7]

通过从最后开始循环遍历,当项被删除(或添加)时,它不会影响列表中较早的项的索引。因此,此示例将正确删除所有甚至来自 alist 的项目。

将元素插入或追加到正在迭代的列表时会出现类似的问题,这会导致无限循环:

alist = [0, 1, 2]
for index, value in enumerate(alist):
    # break to avoid infinite loop:
    if index == 20:     
        break           
    alist.insert(index, 'a')
print(alist)
# Out (abbreviated): ['a', 'a', ..., 'a', 'a',  0,   1,   2]

如果没有 break 条件,只要计算机内存不足并允许程序继续运行,循环就会插入'a'。在这种情况下,通常首选创建一个新列表,并在循环原始列表时将项添加到新列表中。

使用 for 循环时,不能使用占位符变量修改列表元素

alist = [1,2,3,4]
for item in alist:
    if item % 2 == 0:
        item = 'even'
print(alist)
# Out: [1,2,3,4]

在上面的示例中,更改 item 实际上不会更改原始列表中的任何内容。你需要使用列表索引(alist[2]),enumerate() 适用于此:

alist = [1,2,3,4]
for index, item in enumerate(alist):
    if item % 2 == 0:
        alist[index] = 'even'
print(alist)
# Out: [1, 'even', 3, 'even']

一个 while 循环可能会在某些情况下,更好的选择:

如果要删除列表中的所有项目

zlist = [0, 1, 2]
while zlist:
    print(zlist[0])
    zlist.pop(0)
print('After: zlist =', zlist)

# Out: 0
#      1
#      2
# After: zlist = []

虽然简单地重置 zlist 将完成相同的结果;

zlist = []

以上示例也可以与 len() 结合使用以在某个点之后停止,或者删除列表中除 x 之外的所有项目:

zlist = [0, 1, 2]
x = 1
while len(zlist) > x:
    print(zlist[0])
    zlist.pop(0)
print('After: zlist =', zlist)

# Out: 0
#      1
# After: zlist = [2]

或者在删除满足特定条件的元素时循环遍历列表 (在这种情况下,删除所有偶数元素):

zlist = [1,2,3,4,5]
i = 0
while i < len(zlist):
    if zlist[i] % 2 == 0:
        zlist.pop(i)
    else:
        i += 1
print(zlist)
# Out: [1, 3, 5]

请注意,删除元素后不要增加 i。通过删除 zlist[i] 中的元素,下一个项目的索引减少了 1,因此通过在下一次迭代中检查 zlist[i] 具有相同的 i 值,你将正确检查列表中的下一个项目。

考虑从列表中删除不需要的项目的相反方法是将所需项目添加到新列表。以下示例是后一个 while 循环示例的替代示例:

zlist = [1,2,3,4,5]

z_temp = []
for item in zlist:
    if item % 2 != 0:
        z_temp.append(item)
zlist = z_temp
print(zlist)
# Out: [1, 3, 5]

在这里,我们将所需的结果汇集到一个新的列表中。然后,我们可以选择将临时列表重新分配给原始变量。

通过这种思维趋势,你可以调用 Python 最优雅和最强大的功能之一,列表推导,从而消除临时列表,并与先前讨论的就地列表/索引变异意识形态不同。

zlist = [1,2,3,4,5]
[item for item in zlist if item % 2 != 0]
# Out: [1, 3, 5]