В коде есть две проблемы. Первая проблема заключается в том, о чем вы явно спрашиваете: версия для ознакомления в списке будет назначать целую пучку значений None
для self.elements
. Значения None
- это просто возвращаемые значения от ваших вызовов до list.remove
. Он изменяет список на месте и не имеет ничего полезного для возврата (поэтому он возвращает None
).
Осмысление [element for element in elements if elements.count(element) == 1 or elements.remove(element)]
будет работать так же, как ваш другой код (с None
является falsey и or
короткого замыкания), но она по-прежнему работает на второй вопрос. (Это также немного уродливый взлом: новый список, созданный постиганием, будет иметь такое же содержимое, как elements
, так как remove
изменяет elements
на месте, и это довольно запутанно. Написание трудно понять код, как правило, не очень хорошая идея.)
Вторая проблема заключается в том, что изменение списка во время итерации по нему может вызвать проблемы. Список итераторов работает по индексу. Первый элемент, полученный итератором, - это индекс 0, второй - индекс 1 и т. Д. Если вы измените список, удалив элемент в начале списка, вы переместите индексы всех последующих элементов.
Итак, скажем, вы удаляете первый элемент (из индекса 0) сразу после того, как ваш итератор показал его вам.В списке будут перенесены все более поздние значения, но итератор об этом не узнает. Он по-прежнему будет давать элемент в индексе 1 далее, даже если это был элемент в индексе 2 (до изменения списка). Элемент первоначально в индексе 1 (и при индексе 0 после удаления предыдущего элемента) будет пропущен итерацией.
Вот простой пример этого вопроса, в котором значение 2, 5 и 8 не будет печататься:
L = list(range(10)) # [0,1,2,3,4,5,6,7,8,9]
for x in L:
print(x)
if x % 3 == 1: # true for 1,4, and 7
L.remove(x)
В примере, логика для удаления значений довольно проста, и мы никогда не пропустить значение, которое мы обычно хотели бы удалить (поэтому L
имеет ожидаемое значение [0,2,3,5,6,8,9]
в конце), другой код может работать не так хорошо.
Способ избежать этой проблемы - перебрать копию списка при изменении оригинала. В этой ситуации мы также должны count
в оригинале, а не копии:
for element in elements[:]: # copy list with a slice here!
if elements.count(element) > 1:
elements.remove(element) # modify the original list
Это довольно неэффективно, хотя, так как удаление элемента из списка (в положении, чем в конце) необходимо найдите время, чтобы сдвинуть все более поздние значения на одну позицию. Подсчет также медленный, так как вам нужно перебрать весь список для каждого элемента. Это гораздо более эффективно отслеживать уникальные предметы, которые вы видели до сих пор, и пропустить дублированные элементы, когда вы видите их позже:
seen = set()
results = []
for element in elements:
if element not in seen:
seen.add(element)
results.append(element)
Вы можете даже создать несколько неудобного список понимание (с побочными эффектами) из этот код:
seen = set()
results = [element for element in elements
if not (element in seen or seen.add(element))]
лучший подход, как правило, для объединения дедуплицирующей логики в функцию генератора (например, в unique_everseen
recipe в документации itertools
), а затем вызвать его с list(dedupe(elements))
.
Ни одна из версий вашего кода не сделает то, что вы хотите. Мутировав список, когда вы повторяете его, пропустите некоторые значения! – Blckknght
Возможный дубликат [Удалить элементы из списка при повторении в Python] (http://stackoverflow.com/questions/1207406/remove-items-from-a-list-while-iterating-in-python) – Makoto
Что относительно списка (набор (элементы))? – rebeling