Python删除for循环中的元素

Python remove element in for loop

本问题已经有最佳答案,请猛点这里访问。

[注意:即使使用临时数组(不修改循环数组),代码也没有给出列表EDOCX1[0]的正确答案。]

从列表中删除奇数,请参阅下面的代码。为什么它适用于list a,而不是list b?谢谢您。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def purify(numbers):
    numbers_buf = numbers
    for item in numbers:
        if item % 2 == 1:
            numbers_buf.remove(item)
    return numbers_buf

a = [1,2,3,4]
print purify(a)
[2, 4]

b = [4,5,5,4]
print purify(b)
[4,5,4]


你的问题源于这样一个事实:你没有像你想的那样循环浏览不同的列表。您希望避免在循环遍历列表时从列表中删除某些内容,这就是为什么您使用numbers_buf = numbers创建了一个新的列表。

但是,请注意,没有创建新列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
In [73]: numbers = list(range(10))

In [74]: numbers
Out[74]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [75]: id(numbers)
Out[75]: 4469310792

In [76]: numbers_buf = numbers

In [77]: id(numbers)
Out[77]: 4469310792

In [78]: id(numbers_buf)
Out[78]: 4469310792

In [79]: id(numbers_buf) == id(numbers)
Out[79]: True

In [80]: numbers
Out[80]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [81]: numbers_buf
Out[81]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [82]: numbers.append(10)

In [83]: numbers_buf
Out[83]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

最后请注意,当我将10附加到numbers时,它也会附加到numbers_buf上。这是因为在执行numbers_buf = numbers操作时,python不会创建一个包含数字的新列表;它只创建名称numbers_buf,并使其指向与numbers指向的结构完全相同的结构(这就是id(numbers)所显示的所有内容)。您可以按如下方式解决此问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
In [84]: numbers_buf_new = numbers[:]

In [85]: id(numbers_buf_new)
Out[85]: 4469329032

In [86]: id(numbers_buf_new) == id(numbers)
Out[86]: False

In [87]: numbers
Out[87]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

In [88]: numbers_buf_new
Out[88]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

In [89]: numbers.append(11)

In [90]: numbers
Out[90]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

In [91]: numbers_buf_new
Out[91]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

1
2
3
4
5
6
7
8
9
numbers_buf = numbers[:] #  make a copy not a reference


def purify(numbers):
    numbers_buf = numbers[:]
    for item in numbers:
        if item % 2:
            numbers_buf.remove(item)
    return numbers_buf

在迭代列表时操作/删除元素会更改元素索引,numbers_buf = numbers创建对numbers列表的引用,基本上是指向同一对象的指针,因此从一个对象中删除,从两个对象中删除。

使用numbers[:]创建一个复制/新对象。

运行以下函数有助于说明问题,请参阅3和4如何以某种方式处理运行程序:

1
2
3
4
5
6
7
8
9
def purify(numbers):
    numbers_buf = numbers
    should_be = numbers[:]
    for ind, item in enumerate(numbers):
        print("{} should be at {}, is at {}".format(item,should_be.index(item),ind))
        if item % 2:
            numbers_buf.remove(item)
    return numbers_buf
print(purify([4,5,4,7,3,1]))

如果确实要使用列表组件修改原始对象:

1
2
3
def purify(numbers):
    numbers[:] = [x for x in numbers if not x % 2]
    return numbers

您也可以使用reversed

1
2
3
4
5
def purify(numbers):
    for item in reversed(numbers):
        if item % 2:
            numbers.remove(item)
    return numbers


你做了两件你可能不应该做的事,因为他们都在自找麻烦。

  • 在迭代数组时,永远不要添加或删除数组中的项。这将使Python和您都感到困惑,而且几乎肯定无法实现预期的结果。

  • 无论如何,您确实不应该在适当的位置修改数组。函数没有得到数组的副本;它正在修改调用方传入的实际数组。这可能会让所有相关人员感到惊讶:

    1
    2
    3
    a = [1,2,3,4]
    print purify(a)        #=> [2, 4]
    print a                #=> [2, 4]

  • 您应该构造并返回一个新的数组,并可能更改函数名以指示(类似于sortsorted内置项):

    1
    2
    3
    4
    5
    6
    def purified(a):
      return [n for n in a if n % 2 == 0]

    a = [1,2,3,4]
    print purified(a)      #=> [2, 4]
    print a                #=> [1, 2, 3, 4]

    这已经被问过很多次了,在迭代时查看修改列表

    你真的应该这样做:

    1
    even = [a for a in numbers if a%2 == 0]


    您的功能正以另一种您所期望的方式工作。for循环接受第一个元素,而不是第二个元素等,因此当您删除一个元素时,其他元素会更改它们的位置,当它们前面加上另一个奇数时,可以被它跳过(在您的情况下也会发生这种情况)。

    您可以使用此代码:

    1
    2
    def purify(numbers):
    return [x for x in numbers if x % 2 == 0]

    它将分别返回A和B的[2,4][4,4]


    如果要使用remove方法保留并返回相同的列表,可以尝试以下操作:

    1
    2
    3
    4
    5
    6
    7
    8
    def purify(numbers):

    for item in numbers:
        if item % 2 == 1:
            count = numbers.count(item)
            for i in range(count):
                numbers.remove(item)
    return numbers