List comprehension vs filter vs remove

时间:2017-07-11 19:12:38

标签: python list data-structures filter list-comprehension

后两者如何比前一种解决方案更好?

primes = (1, 2, 3, 5, 7)

# Classic solution
items = list(range(10))
for prime in primes:
    items.remove(prime)
items

# List comprehension
items = list(range(10))
[item for item in items if item not in primes]

# Filter
items = list(range(10))
list(filter(lambda item: item not in primes, items))

这三个例子是我在一本书中看到的,它说第一个解决方案需要O(n * m)时间(n = len(项目),m = len(素数))而后两个采用O (n * 1)时间......导致第一个解决方案的50次比较(实际上稍好一些 - 40次比较),后者只有10次。

我不明白这一点。我不明白它是如何节省时间或内存的。

以下是本书中解释这一点的段落:

  

要从列表中删除或插入单个项目,Python需要复制       整个列表,特别是较大的列表。执行时       这只有一次,当然不是那么糟糕。但是当执行一个大的       删除次数,过滤器或列表理解要快得多       解决方案,因为如果结构合理,它只需要复制列表       一旦。       ....那么例子......       对于大型项目列表,后两者要快得多。这是因为       操作要快得多。要比较使用n = len(项目)和m = len(素数),       第一个是O(m * n)= 5 * 10 = 50个操作,而后两个操作       O(n * 1)= 10 * 1 = 10次操作。

编辑: 这本书没有错。 primes = set((1, 2, 3, 5, 7))是正确的声明,而不是primes = (1, 2, 3, 5, 7)

2 个答案:

答案 0 :(得分:2)

如果图书中的代码与您发布的内容完全相同,则图书错误

第一个例子有时间复杂度O(n*m),但其他两个例子也是如此。

如果primesset(或dict),则情况确实如此 - 在散列映射中使用in运算符进行查找具有时间复杂度{{1 },但在O(1)list中有tuple!因此,O(n)的总复杂性。

让我们通过一些测量来检查:

O(n*m)

请注意,t = tuple(range(10000)) l = list(t) s = set(t) d = {i:1 for i in l} In [16]: %%timeit 4738 in t ....: 10000 loops, best of 3: 45.5 µs per loop In [17]: %%timeit 4738 in l ....: 10000 loops, best of 3: 45.4 µs per loop In [18]: %%timeit 4738 in s ....: 10000000 loops, best of 3: 36.9 ns per loop In [19]: %%timeit 4738 in d ....: 10000000 loops, best of 3: 38 ns per loop 中的查询为set(与~37ns类似),比dict / list快3个数量级,{{ 1}}。

答案 1 :(得分:2)

主要问题来自items.remove(prime)。原因是因为Python的列表是可变长度数组,而不是链表。它们使用连续的内存块来引用其他对象。如果从块中的任何位置插入/移除元素,则块中的所有元素都必须移动到新的连续内存块(一些优化实现用于插入数组的开头或结尾)。 see the documentation here 循环遍历列表len(primes)次,对于每次删除,您遍历项len(items)次。如果元素存在,则将项目列表复制到新的连续块,不包括匹配的项目。将元素复制到新块有隐藏的成本。

另外两个示例遍历列表,将所有当前元素保留为原样。并根据提供的过滤器返回一个新列表。