2014-09-26 3 views
3

Есть ли лучше (очевидно,/идиоматический) способ питона писать эквивалентPython идиома для итерации изменения списка

index = 0 
while index < len(some_list): 
    do_some_stuff(some_list[index]) # may have many side effects 
    if delete_element(some_list[index]): 
     del some_list[index] 
    else: 
     index += 1 

или словарную эквивалент этого для словарей? Dict/list comprehension - это не вещь, потому что результат delete_element может зависеть от do_some_stuff.

ответ

1

Если заказ не имеет значения, вы можете перечислить его в обратном порядке, чтобы удалять не испортили часть списка, который вы еще не обработали.

for i, item in enumerate(reversed(somelist), -len(somelist)+1): 
    do_some_stuff(item) 
    if delete_element(item): 
     del somelist[-i] 

Если заказ имеет значение, отмените список, сделайте этот трюк, а затем снова отмените его. Это смутит их!

В зависимости от ситуации вы можете заменить элемент маркером, таким как None. Либо выпустите его позже, либо используйте другие места, в котором этот список используется.

for i, item in enumerate(somelist)): 
    do_some_stuff(item) 
    if delete_element(item): 
     somelist[i] = None 

somelist = [item for item in somelist if item] 
+0

Ваш вызов 'enumerate' не будет генерировать индексы, которые соответствуют вашим обратным значениям (' somelist [-len (somelist) +1] 'is' somelist [0] '). Вы, вероятно, хотите «перечислить (обратный (somelist), 1)», а затем «del somelist [-index]». – Blckknght

+0

@Blckknght - ваше право, я хотел использовать abs (i). перечисление не учитывается в обратном направлении, поэтому трюк должен начинаться с отрицательного значения и доходить до нуля. Но, как вы говорите, использование отрицательного индекса удаляет неправильное значение. - исправлено в коде. – tdelaney

+0

А, это работает лучше. Однако я бы использовал '-i', а не' abs (i) ', поскольку последний выполняет проверку, чтобы увидеть, какой знак имеет значение, которое вам не нужно (вы всегда хотите отрицать существующее значение). – Blckknght

3

Вы можете разбить операции на две отдельные петли и использовать представление списка для второй части.

for value in some_list: 
    do_some_stuff(value) 

some_list = [value for value in some_list if not delete_element(value)] 

Другим решением было бы перебрать копию списка и использовать enumerate для отслеживания индексов без необходимости поддерживать счетчик вручную.

for index, value in enumerate(some_list[::-1]): 
    do_some_stuff(value) 
    if delete_element(value): 
     del some_list[-index - 1] 

Вы хотели бы итерацию назад, так что вам не придется настраивать index для удаленных элементов.

+2

'enumerate' подсчитывает от нуля, даже если вы вызываете его на обратном итератора. Вам нужно использовать 'del some_list [-index-1]', чтобы удаленные индексы отсчитывали вместе с элементами. – Blckknght

0

вы могли бы даже, если предположить, что do_some_stuff возвращается item, сделать что-то вроде:

res = [item for item in some_list if not delete_element(do_some_stuff(item))] 
0

без излишеств способ заключается в использовании генератор:

def do_stuff_and_filter_items(items): 
    for item in items: 
     do_some_stuff(item) 
     if not delete_element(item): 
      yield item 

Тогда, чтобы получить истребитель:

items = do_stuff_and_filter_items(items) 

или получить список:

items = list(do_stuff_and_filter_items(items)) 

или перезаписать старый список:

items[:] = do_stuff_and_filter_items(items) 
Смежные вопросы