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

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

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


当前回答

您可能希望使用内置的filter()。

更多详情请点击这里

其他回答

最有效的方法是列表理解,很多人展示了他们的案例,当然,通过过滤器获得迭代器也是一个很好的方法。

过滤器接收一个函数和一个序列。Filter依次将传递的函数应用于每个元素,然后根据函数的返回值是True还是False来决定是否保留或丢弃该元素。

这里有一个例子(获取元组中的概率):

list(filter(lambda x:x%2==1, (1, 2, 4, 5, 6, 9, 10, 15)))  
# result: [1, 5, 9, 15]

警告:你也可以不处理迭代器。迭代器有时比序列更好。

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

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

or

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

我能想到三种方法来解决你的问题。例如,我将创建元组somelist =[(1,2,3),(4,5,6),(3,6,6),(7,8,9),(15,0,0),(10,11,12)]的随机列表。我选择的条件是元组元素之和= 15。在最终的列表中,我们将只有那些和不等于15的元组。

我所选择的是一个随机选择的例子。请随意更改元组列表和我所选择的条件。

方法1。使用你建议的框架(在for循环中填充代码)。我使用一个带del的小代码来删除满足上述条件的元组。然而,如果两个连续放置的元组满足给定条件,该方法将错过一个元组(满足上述条件)。

for tup in somelist:
    if ( sum(tup)==15 ): 
        del somelist[somelist.index(tup)]

print somelist
>>> [(1, 2, 3), (3, 6, 6), (7, 8, 9), (10, 11, 12)]

方法2。构造一个新的列表,其中包含不满足给定条件的元素(元组)(这与删除满足给定条件的列表元素是一样的)。下面是它的代码:

newlist1 = [somelist[tup] for tup in range(len(somelist)) if(sum(somelist[tup])!=15)]

print newlist1
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]

方法3。找到满足给定条件的索引,然后使用与这些索引对应的删除元素(元组)。下面是它的代码。

indices = [i for i in range(len(somelist)) if(sum(somelist[i])==15)]
newlist2 = [tup for j, tup in enumerate(somelist) if j not in indices]

print newlist2
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]

方法1和方法2比方法3快。方法2和方法3比方法1更有效。我更喜欢方法2。对于上面的例子,time(method1): time(method2): time(method3) = 1:1: 1.7

变通方案概述

:

use a linked list implementation/roll your own. A linked list is the proper data structure to support efficient item removal, and does not force you to make space/time tradeoffs. A CPython list is implemented with dynamic arrays as mentioned here, which is not a good data type to support removals. There doesn't seem to be a linked list in the standard library however: Is there a linked list predefined library in Python? https://github.com/ajakubek/python-llist start a new list() from scratch, and .append() back at the end as mentioned at: https://stackoverflow.com/a/1207460/895245 This time efficient, but less space efficient because it keeps an extra copy of the array around during iteration. use del with an index as mentioned at: https://stackoverflow.com/a/1207485/895245 This is more space efficient since it dispenses the array copy, but it is less time efficient, because removal from dynamic arrays requires shifting all following items back by one, which is O(N).

一般来说,如果你做得很快,不想添加一个自定义LinkedList类,你只需要在默认情况下使用更快的.append()选项,除非内存是一个大问题。

官方Python 2教程4.2。“声明”

https://docs.python.org/2/tutorial/controlflow.html#for-statements

这部分文档明确说明:

您需要复制迭代列表才能修改它 一种方法是使用切片符号[:]

If you need to modify the sequence you are iterating over while inside the loop (for example to duplicate selected items), it is recommended that you first make a copy. Iterating over a sequence does not implicitly make a copy. The slice notation makes this especially convenient: >>> words = ['cat', 'window', 'defenestrate'] >>> for w in words[:]: # Loop over a slice copy of the entire list. ... if len(w) > 6: ... words.insert(0, w) ... >>> words ['defenestrate', 'cat', 'window', 'defenestrate']

Python 2文档7.3。“for语句”

https://docs.python.org/2/reference/compound_stmts.html#for

这部分文档再次说明你必须复制一份,并给出了一个实际的删除示例:

Note: There is a subtlety when the sequence is being modified by the loop (this can only occur for mutable sequences, i.e. lists). An internal counter is used to keep track of which item is used next, and this is incremented on each iteration. When this counter has reached the length of the sequence the loop terminates. This means that if the suite deletes the current (or a previous) item from the sequence, the next item will be skipped (since it gets the index of the current item which has already been treated). Likewise, if the suite inserts an item in the sequence before the current item, the current item will be treated again the next time through the loop. This can lead to nasty bugs that can be avoided by making a temporary copy using a slice of the whole sequence, e.g., for x in a[:]:

    if x < 0: a.remove(x)

然而,我不同意这个实现,因为.remove()必须遍历整个列表才能找到值。

Python能做得更好吗?

似乎这个特定的Python API可以得到改进。例如,将其与:

Java ListIterator::删除哪些文档“此调用只能对next或previous调用一次” c++ std::vector::erase,返回被删除元素之后的一个有效的互操作器

这两种方法都清楚地表明,除了使用迭代器本身,您不能修改正在迭代的列表,并为您提供了在不复制列表的情况下修改列表的有效方法。

可能潜在的基本原理是,Python列表被假定为动态数组支持,因此任何类型的删除都将是低效的,而Java在ListIterator的ArrayList和LinkedList实现方面都有更好的接口层次结构。

在Python标准库中似乎也没有显式的链表类型:Python链表

在某些情况下,您所做的不仅仅是一次过滤一个列表项,您希望在迭代时更改迭代。

这里有一个例子,事先复制列表是不正确的,反向迭代是不可能的,列表理解也是一个选项。

""" Sieve of Eratosthenes """

def generate_primes(n):
    """ Generates all primes less than n. """
    primes = list(range(2,n))
    idx = 0
    while idx < len(primes):
        p = primes[idx]
        for multiple in range(p+p, n, p):
            try:
                primes.remove(multiple)
            except ValueError:
                pass #EAFP
        idx += 1
        yield p