我有一个清单:
my_list = ['a', 'b', 'c', 'a', 'b', 'c', 'a']
我使用以下代码删除不符合要求的元素:
[my_list.remove(element) for element in my_list if 'a' not in element]
但不是预期['a', 'a', 'a']
获得['a', 'c', 'a', 'c', 'a']
。似乎在删除'b'
Python后没有检查'c'
个元素......
请告诉我如何解决此问题并有效地从列表中删除所有不必要的元素。
答案 0 :(得分:8)
其他答案解决了这个问题,但让我解释一下这里发生了什么。
>>> lst = ['a', 'b', 'c', 'a', 'b', 'c', 'a']
>>> for each in lst:
... if 'a' not in each:
... lst.remove(each)
>>> lst
['a', 'c', 'a', 'c', 'a']
# V - Current position of loop
# ['a', 'b', 'c', 'a', 'b', 'c', 'a']
if 'a' not in each: #Output False
# V - Current position of loop
# ['a', 'b', 'c', 'a', 'b', 'c', 'a']
if 'a' not in each: #Output True
list.remove(each) #Element from position 1 ('b') in list is removed
# V |___ Supposed to be like this
# ['a', 'b', 'c', 'a', 'b', 'c', 'a'] |
# V |___ Updated list
# ['a', 'c', 'a', 'b', 'c', 'a'] |
if 'a' not in each: #Output False
这就是为什么你的' c'在输出列表中跳过。
现在要解决您的问题,而不是删除所有非a
,这是创建仅包含a
的列表的更好方法。 (Trengot's Answer)
由于您的my_list
是字符集合,因此最好使用if 'a' != element
,因为'a' not in element
将扫描元素的每个字母,并且还会删除所有带字母的元素'a'
(Check this to understand how in
works in Python)。
例如,如果您的my_list = ['a','abc','fd','b','c']
,'a' not in 'abc'
将返回False
,则元素'abc'
将无法删除。
答案 1 :(得分:3)
将列表过滤为新列表,选择您想要的元素,而不是删除不需要的元素。然后使用新的或将其分配给旧的。
my_list = [element for element in my_list if 'a' in element]
正如Peter Wood指出的那样,这将为my_list
分配一个新对象。如果您想保留相同的列表对象(例如,如果它也在其他地方引用),请将新列表分配给my_list[:]
。
my_list[:] = [element for element in my_list if 'a' in element]
答案 2 :(得分:1)
由于您想要就地修改(缩小)现有列表,所以这里有以下内容:
def remove_all_on_predicate(predicate, list_):
deserving_removal = [elem for elem in list_ if predicate(elem)]
for elem in deserving_removal:
list_.remove(elem)
return None
>>> remove_all_on_predicate(lambda x: "a" not in x, my_list)
>>> my_list
['a', 'a', 'a']
答案 3 :(得分:1)
正如您所发现的那样,尝试从正在迭代的列表中删除元素可能无法达到预期效果。 Ashwani Agarwal的回答说明了它失败的原因,其他答案显示了可用于正确执行删除的各种技术。当你有一个你无法复制的非常大的列表时,另一种有用的技术就是反过来迭代它:
my_list = ['a', 'b', 'c', 'a', 'b', 'c', 'a']
for element in reversed(my_list):
if 'a' not in element:
my_list.remove(element)
print(element, my_list)
print('Final:', my_list)
my_list = ['a', 'b', 'c', 'a', 'b', 'c', 'a']
for element in reversed(my_list):
if 'a' in element:
my_list.remove(element)
print(my_list)
print('Final:', my_list)
<强>输出强>
c ['a', 'b', 'a', 'b', 'c', 'a']
c ['a', 'b', 'a', 'b', 'a']
b ['a', 'a', 'b', 'a']
b ['a', 'a', 'a']
Final: ['a', 'a', 'a']
['b', 'c', 'a', 'b', 'c', 'a']
['b', 'c', 'b', 'c', 'a']
['b', 'c', 'b', 'c']
Final: ['b', 'c', 'b', 'c']
此代码使用reversed()
函数,该函数返回传递给它的迭代器上的迭代器;它不会复制可迭代的。
我应该提一下,这种技术的效率低于其他答案中给出的过滤方法。这是因为my_list.remove(element)
的每次调用都必须扫描my_list
,直到找到匹配的元素,因此它具有复杂度O(n ** 2),其中n
是数字列表中的元素;滤波算法具有O(n)的复杂度。正如我之前所说,这种方法仅在列表太大以至于无法提供RAM来创建新列表时才有用。
我还需要提一下你的问题中的代码:当你应该使用普通的for
循环时,你正在使用列表推导来遍历列表。 list.remove()
会返回None
,因此您的代码会不必要地创建一个充满None
s的列表,然后将该列表丢弃。一般规则是:不要仅仅使用列表理解来调用你在其中调用的函数的副作用。
答案 4 :(得分:0)
我使用filter
my_list = filter(lambda x: 'a' in x, my_list)