从列表中删除某些重复元素的最佳(Pythonic)方法是什么?

时间:2018-09-27 21:22:26

标签: python python-3.x

考虑以下示例:

In [1]: lst = list(range(10)) * 2

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

In [3]: for i, x in enumerate(list(lst)):
   ...:     if i > 10 and x % 2:
   ...:         lst.remove(x)
   ...:         

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

因此该策略不起作用,因为它删除了list中第一次出现的项目,而这并不是我想要的。

In [5]: lst = list(range(10)) * 2

In [6]: for i, x in enumerate(list(lst)):
   ...:     if i > 10 and x % 2:
   ...:         del lst[i]
   ...:         
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-6-bbec803a1844> in <module>()
      1 for i, x in enumerate(list(lst)):
      2     if i > 10 and x % 2:
----> 3         del lst[i]
      4 

IndexError: list assignment index out of range

我的其他策略也不起作用,因为最初的list的副本最终具有更大的索引,因为原始的list会不断删除其内容。

以下方法有效:

In [7]: lst = list(range(10)) * 2

In [8]: idx = 0

In [9]: for i, x in enumerate(list(lst)):
   ...:     if i > 10 and x % 2:
   ...:         lst.pop(i-idx)
   ...:         idx += 1
   ...:         

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

还有这个:

In [11]: lst = list(range(10)) * 2

In [12]: idx = 0

In [13]: for i, li in enumerate(x for x in lst.copy()):
    ...:     if i > 10 and li % 2:
    ...:         lst.pop(i-idx)
    ...:         idx += 1
    ...:         

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

其中一种工作方法是否比其他方法更好?有没有更好的方法来实现我想要的?如果我在or测试中有if条语句怎么办?

In [15]: lst = list(range(10)) * 2

In [16]: idx = 0

In [17]: for i, x in enumerate(list(lst)):
    ...:     if i > 10 and (x % 2 or x // 5):
    ...:         lst.pop(i-idx)
    ...:         idx += 1
    ...:

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

2 个答案:

答案 0 :(得分:4)

每当您发现编写for循环来构造或修改列表时,请问自己如何使用列表理解来实现。列表理解是Python的。

>>> [x for i, x in enumerate(lst) if i <= 10 or x % 2 == 0]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 2, 4, 6, 8]

答案 1 :(得分:1)

上面的解决方案将是我使用 list comprehension 的首选,但是对于另一种解决方案,即使只是为了学习新工具,我们也可以使用filterfalse

from itertools import filterfalse

l = [*range(10)]*2
l = list(filterfalse(lambda x: x[0] > 10 and x[1] % 2, enumerate(l)))
print([y for x, y in l])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 2, 4, 6, 8]