我在Python中迭代一个元组列表,并试图删除它们,如果它们满足某些条件。

for tup in somelist:
    if determine(tup):
         code_to_remove_tup

我应该用什么来代替code_to_remove_tup?我不知道怎么用这种方式把东西拿掉。


当前回答

对于喜欢函数式编程的人:

somelist[:] = filter(lambda tup: not determine(tup), somelist)

or

from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))

其他回答

如果您想在迭代期间做其他事情,那么最好同时获得索引(这保证您能够引用它,例如,如果您有一个字典列表)和实际的列表项内容。

inlist = [{'field1':10, 'field2':20}, {'field1':30, 'field2':15}]    
for idx, i in enumerate(inlist):
    do some stuff with i['field1']
    if somecondition:
        xlist.append(idx)
for i in reversed(xlist): del inlist[i]

Enumerate使您可以同时访问项和索引。反向是为了以后你要删除的索引不会改变。

建议列表推导的答案几乎是正确的,除了它们构建了一个全新的列表,然后给它一个与旧列表相同的名称,它们没有在适当的地方修改旧列表。这与Lennart建议的选择性删除不同——它更快,但如果您的列表是通过多个引用访问的,那么您只是重新设置了其中一个引用,而没有更改列表对象本身,这可能会导致微妙的、灾难性的错误。

幸运的是,它非常容易获得列表推导式的速度和所需的就地更改的语义——只是代码:

somelist[:] = [tup for tup in somelist if determine(tup)]

请注意与其他答案的细微区别:这个答案没有分配给一个裸名。它赋值给一个列表切片,恰好是整个列表,因此替换了同一Python列表对象中的列表内容,而不是像其他答案一样只是重新设置一个引用(从以前的列表对象到新的列表对象)。

这个答案最初是为了回答一个问题而写的,这个问题后来被标记为重复: 在python中从列表中删除坐标

在你的代码中有两个问题:

1)当使用remove()时,你试图删除整数,而你需要删除一个元组。

2) for循环将跳过列表中的项目。

让我们来看看当我们执行你的代码时会发生什么:

>>> L1 = [(1,2), (5,6), (-1,-2), (1,-2)]
>>> for (a,b) in L1:
...   if a < 0 or b < 0:
...     L1.remove(a,b)
... 
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
TypeError: remove() takes exactly one argument (2 given)

第一个问题是同时向remove()传递'a'和'b',但remove()只接受一个参数。那么如何才能让remove()正确地处理列表呢?我们需要算出列表中的每个元素是什么。在本例中,每一个都是元组。为了看到这一点,让我们访问列表中的一个元素(索引从0开始):

>>> L1[1]
(5, 6)
>>> type(L1[1])
<type 'tuple'>

啊哈!L1中的每个元素实际上都是一个元组。这就是我们需要传递给remove()的东西。python中的元组非常简单,只需将值括在括号中即可。"a, b"不是元组,但"(a, b)"是元组。所以我们修改你的代码并再次运行:

# The remove line now includes an extra "()" to make a tuple out of "a,b"
L1.remove((a,b))

这段代码运行时没有任何错误,但是让我们看看它输出的列表:

L1 is now: [(1, 2), (5, 6), (1, -2)]

为什么(1,-2)还在列表中?事实证明,如果没有特别注意,在使用循环遍历列表的同时修改列表是一个非常糟糕的主意。(1, -2)保留在列表中的原因是列表中每个项的位置在for循环的迭代之间发生了变化。让我们看看如果我们给上面的代码提供一个更长的列表会发生什么:

L1 = [(1,2),(5,6),(-1,-2),(1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
### Outputs:
L1 is now: [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]

正如您可以从结果中推断的那样,每当条件语句求值为true并且删除一个列表项时,循环的下一次迭代将跳过列表中下一项的求值,因为它的值现在位于不同的下标处。

最直观的解决方案是复制列表,然后遍历原始列表,只修改副本。你可以试着这样做:

L2 = L1
for (a,b) in L1:
    if a < 0 or b < 0 :
        L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
print L2 is L1
del L1
L1 = L2; del L2
print ("L1 is now: ", L1)

然而,输出将与之前相同:

'L1 is now: ', [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]

这是因为当我们创建L2时,python实际上并没有创建一个新对象。相反,它只是将L2引用到与L1相同的对象。我们可以用“is”来验证这一点,它不同于仅仅的“equals”(==)。

>>> L2=L1
>>> L1 is L2
True

我们可以使用copy.copy()创建一个真正的副本。然后一切都按照预期进行:

import copy
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
L2 = copy.copy(L1)
for (a,b) in L1:
    if a < 0 or b < 0 :
        L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
del L1
L1 = L2; del L2
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]

最后,有一个比必须制作一个全新的L1副本更干净的解决方案。reversed()函数:

L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
for (a,b) in reversed(L1):
    if a < 0 or b < 0 :
        L1.remove((a,b))
print ("L1 is now: ", L1)
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]

不幸的是,我无法充分描述reversed()是如何工作的。当一个列表被传递给它时,它返回一个'listreverseiterator'对象。出于实际目的,您可以将其视为创建其参数的反向副本。这是我推荐的解决方案。

for i in range(len(somelist) - 1, -1, -1):
    if some_condition(somelist, i):
        del somelist[i]

你需要向后走,否则就有点像锯掉你坐着的树枝:-)

Python 2用户:用xrange替换range以避免创建硬编码的列表

for循环将遍历一个索引…

假设你有一个清单,

[5, 7, 13, 29, 65, 91]

您使用了一个名为lis的列表变量。你用同样的方法来移除…

你的变量

lis = [5, 7, 13, 29, 35, 65, 91]
       0  1   2   3   4   5   6

在第5次迭代中,

你的数字35不是质数,所以你把它从列表中删除了。

lis.remove(y)

然后下一个值(65)移动到前一个索引。

lis = [5, 7, 13, 29, 65, 91]
       0  1   2   3   4   5

所以第四次迭代完成指针移动到第五次…

这就是为什么你的循环没有覆盖65,因为它已经移动到前一个索引。

因此,你不应该将一个列表引用到另一个变量中,它仍然引用原始变量而不是副本。

ite = lis # Don’t do it will reference instead copy

所以使用list[::]复制列表。

现在你会付出,

[5, 7, 13, 29]

问题是你在迭代过程中从列表中删除了一个值,然后你的列表索引就会崩溃。

所以你可以试试列表理解。

它支持所有的可迭代对象,如list, tuple, dict, string等。