Python не удаляет все элементы из списка во время итерации по списку - PullRequest
0 голосов
/ 24 февраля 2019

У меня есть словарь и список, как показано ниже

correction =  {u'drug.ind': u'Necrosis', "date": "exp"}
drugs =  [[u'drug.aus', u'Necrosis'], [u'drug.nz', u'Necrosis'], [u'drug.uk', u'Necrosis'], [u'drug.ind', u'Necrosis'], [u'cheapest', u'drug.ind'], [u'date', u'']]

Теперь в основном я смотрю значение корректирующего словаря и всякий раз, когда оно соответствует каждому второму элементу списков в списке drugs, я удаляюих.

Это то, что я делаю

if correction and drugs:
    for i,x in correction.items():
        for j,k in enumerate(drugs):
            if len(i.split(".")) > 1:  # need to do the operation only for drugs which is always given in this format
                if x == k[1]:
                    drugs.pop(j)

В идеале список drugs теперь должен выглядеть так:

drugs = [['cheapest', 'drug.ind'], ['date', '']]

Но по какой-то причине он выглядит как

[['drug.nz', 'Necrosis'], ['drug.ind', 'Necrosis'], ['cheapest', 'drug.ind'], ['date', '']]

Я надеялся, что все, что похоже на Некроз , будет удалено.Но он удаляет его альтернативно.

Почему я сталкиваюсь с таким поведением?Что я делаю не так?

Ответы [ 4 ]

0 голосов
/ 24 февраля 2019

Вы перебираете список (drugs), и внутри цикла вы удаляете элементы из того же списка.

Когда цикл for выполняется над повторяемым объектом, Python сохраняетувеличивая внутреннюю переменную «index», которая помогает Python отслеживать текущий элемент списка, в котором мы находимся.

Внутри цикла, скажем, вы удалили элемент с index = 3. Теперь,остальная часть списка (элементы, которые вы еще не перебрали) сместится на одно место.Элемент, который ранее присутствовал в индексе 4, теперь будет присутствовать в индексе 3, освобожденном удаленным элементом.Чтобы обработать этот сдвинутый элемент в следующей итерации, внутренняя переменная «index» должна еще раз принять значение 3 для следующей итерации.Но Python увеличивает индексную переменную с 3 до 4 для следующей итерации, как это обычно бывает с одной итерации в другую.В результате элемент, следующий непосредственно за удаленным элементом, не будет проверен / обработан телом вашего цикла for (поскольку индекс будет равен 4, а не 3), и, следовательно, он не будет удален, даже если он соответствует критериям удаления..

Несколько решений

Существует несколько методов, предложенных для "безопасного" удаления, в этом потоке .

IЯ выбрал мой любимый из них и реализовал его для вашего кода ниже:

correction =  {u'drug.ind': u'Necrosis', "date": "exp"}
drugs =  [[u'drug.aus', u'Necrosis'], [u'drug.nz', u'Necrosis'], [u'drug.uk', u'Necrosis'],
          [u'drug.ind', u'Necrosis'], [u'cheapest', u'drug.ind'], [u'date', u'']]

if correction and drugs:
    for i,x in correction.items():
        for j in range(len(drugs)-1, -1, -1):
            if len(i.split(".")) > 1:  # need to do the operation only for drugs which is always given in this format
                if x == drugs[j][1]:
                    drugs.pop(j)
print(drugs)

Вывод этого:

[['cheapest', 'drug.ind'], ['date', '']]

Важнейшим аспектом этого решения являетсялиния for j in range(len(drugs)-1, -1, -1).Сейчас мы перебираем индексы вместо позиций по этим индексам.И мы перебираем индексы в обратном порядке (что фактически означает, что мы косвенно обрабатываем список в обратном порядке).

0 голосов
/ 24 февраля 2019

Как уже упоминалось другими, вы не должны изменять список или другие итерируемые, когда вы перебираете его.Если вы хотите удалить определенные элементы, вы должны создать список тех элементов, которые вы хотите удалить, и затем удалить их:

bad = []
for j, k in enumerate(drugs):
    if len(i.split(".")) > 1:
        if x == k[1]:
            bad.append(k)
for item in bad:
    drugs.remove(item)

Как упомянуто на sourcehead , это решение может потерпеть неудачу, если в drugs есть равные элементы, причем некоторые из них удаляются, а другие нет, если сам индекс является частью условия.Более общее решение может быть таким:

import itertools

bad = []
for j, k in enumerate(drugs):
    if len(i.split(".")) > 1 and x == k[1]:
        bad.append(True)
    else:
        bad.append(False)
drugs = list(itertools.compress(drugs, bad))
0 голосов
/ 24 февраля 2019

Вы можете создать набор из значений словаря correction (для быстрого поиска) и использовать функцию filter() для фильтрации списка:

corr = set(correction.values())

list(filter(lambda x: x[1] not in corr, drugs))
# [['cheapest', 'drug.ind'], ['date', '']]
0 голосов
/ 24 февраля 2019

Потому что, когда вы извлекаете элемент из массива, он меняет индекс следующего элемента в списке, чтобы быть «позади» итератора.

В приведенном ниже примере вы видите, что мы только когда-либо выполняемprint () для каждого другого элемента в массиве, хотя, несмотря на это, мы перебираем массив, удаляя все элементы, в итоге мы удаляем только половину

example = ['apple','banana','carrot','donut','edam','fromage','ghee','honey']

for index,food in enumerate(example):
    print(food);
    example.pop(index)

print(example) 

Это потому, что дляцикл (в основном) выполняет увеличение целого числа i на каждом цикле и получение example[i] при извлечении элементов из example, это меняет положение более поздних элементов, поэтому example[i] изменяется.

ЭтоКод демонстрирует этот факт. Как вы видите после того, как мы «вытолкнули» элемент, следующий элемент меняется на наших глазах.

example = ['apple','banana','carrot','donut','edam','fromage','ghee','honey']


for i in range(0,len(example)-1):
    print("The value of example[",i,"] is: ",example[i+1])
    example.pop(i)
    print("after popping ,the value of example[",i,"] is: ",example[i+1])

print(example)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...