TLDR:
Я написал библиотеку, которая позволяет вам сделать это:
from fluidIter import FluidIterable
fSomeList = FluidIterable(someList)
for tup in fSomeList:
if determine(tup):
# remove 'tup' without "breaking" the iteration
fSomeList.remove(tup)
# tup has also been removed from 'someList'
# as well as 'fSomeList'
Лучше использовать другой метод, если это возможно, который не требует модификации вашего итерируемого при его повторении, но для некоторых алгоритмов это может быть не так просто. И поэтому, если вы уверены, что действительно хотите код шаблона, описанный в исходном вопросе, это возможно.
Должен работать со всеми изменяемыми последовательностями, а не только со списками.
Полный ответ:
Редактировать: последний пример кода в этом ответе дает вариант использования для , почему вы можете иногда захотеть изменить список на месте, а не использовать его понимание. Первая часть ответов служит учебником по как массив может быть изменен на месте.
Решение следует из этого ответа (для связанного вопроса) от senderle. Что объясняет, как обновляется индекс массива при переборе списка, который был изменен. Приведенное ниже решение предназначено для правильного отслеживания индекса массива, даже если список изменен.
Скачать fluidIter.py
с здесь https://github.com/alanbacon/FluidIterator
, это всего лишь один файл, поэтому не нужно устанавливать git. Установщика не существует, поэтому вам нужно убедиться, что файл находится в пути python. Код был написан для Python 3 и не тестировался на Python 2.
from fluidIter import FluidIterable
l = [0,1,2,3,4,5,6,7,8]
fluidL = FluidIterable(l)
for i in fluidL:
print('initial state of list on this iteration: ' + str(fluidL))
print('current iteration value: ' + str(i))
print('popped value: ' + str(fluidL.pop(2)))
print(' ')
print('Final List Value: ' + str(l))
Это даст следующий вывод:
initial state of list on this iteration: [0, 1, 2, 3, 4, 5, 6, 7, 8]
current iteration value: 0
popped value: 2
initial state of list on this iteration: [0, 1, 3, 4, 5, 6, 7, 8]
current iteration value: 1
popped value: 3
initial state of list on this iteration: [0, 1, 4, 5, 6, 7, 8]
current iteration value: 4
popped value: 4
initial state of list on this iteration: [0, 1, 5, 6, 7, 8]
current iteration value: 5
popped value: 5
initial state of list on this iteration: [0, 1, 6, 7, 8]
current iteration value: 6
popped value: 6
initial state of list on this iteration: [0, 1, 7, 8]
current iteration value: 7
popped value: 7
initial state of list on this iteration: [0, 1, 8]
current iteration value: 8
popped value: 8
Final List Value: [0, 1]
Выше мы использовали метод pop
для объекта списка флюидов. Также реализованы другие распространенные итерируемые методы, такие как del fluidL[i]
, .remove
, .insert
, .append
, .extend
. Список также можно изменить с помощью слайсов (методы sort
и reverse
не реализованы).
Единственным условием является то, что вы должны только изменить список на месте, если в любой момент fluidL
или l
были переназначены другому объекту списка, код не будет работать. Исходный объект fluidL
по-прежнему будет использоваться циклом for, но мы не сможем его изменить.
1052 * т.е. *
fluidL[2] = 'a' # is OK
fluidL = [0, 1, 'a', 3, 4, 5, 6, 7, 8] # is not OK
Если мы хотим получить доступ к текущему значению индекса списка, мы не можем использовать перечисление, так как это только подсчитывает, сколько раз цикл for был выполнен. Вместо этого мы будем использовать объект итератора напрямую.
fluidArr = FluidIterable([0,1,2,3])
# get iterator first so can query the current index
fluidArrIter = fluidArr.__iter__()
for i, v in enumerate(fluidArrIter):
print('enum: ', i)
print('current val: ', v)
print('current ind: ', fluidArrIter.currentIndex)
print(fluidArr)
fluidArr.insert(0,'a')
print(' ')
print('Final List Value: ' + str(fluidArr))
Будет выведено следующее:
enum: 0
current val: 0
current ind: 0
[0, 1, 2, 3]
enum: 1
current val: 1
current ind: 2
['a', 0, 1, 2, 3]
enum: 2
current val: 2
current ind: 4
['a', 'a', 0, 1, 2, 3]
enum: 3
current val: 3
current ind: 6
['a', 'a', 'a', 0, 1, 2, 3]
Final List Value: ['a', 'a', 'a', 'a', 0, 1, 2, 3]
Класс FluidIterable
просто предоставляет оболочку для исходного объекта списка. Доступ к исходному объекту можно получить как свойство объекта «жидкость», например:
originalList = fluidArr.fixedIterable
Дополнительные примеры / тесты можно найти в разделе if __name__ is "__main__":
внизу fluidIter.py
. На них стоит посмотреть, потому что они объясняют, что происходит в различных ситуациях. Например: замена больших разделов списка с использованием фрагмента. Или используя (и модифицируя) ту же итерацию во вложенных циклах for.
Как я уже сказал, для начала: это сложное решение, которое ухудшит читабельность вашего кода и затруднит его отладку. Поэтому другие решения, такие как списки, упомянутые в ответе Дэвида Разника , должны быть рассмотрены в первую очередь. При этом я нашел случаи, когда этот класс был полезен для меня и его было проще использовать, чем отслеживать индексы элементов, которые необходимо удалить.
Редактировать: Как уже упоминалось в комментариях, этот ответ на самом деле не представляет проблему, для которой этот подход обеспечивает решение. Я постараюсь обратиться к этому здесь:
Понимания списка обеспечивают способ создания нового списка, но эти подходы, как правило, рассматривают каждый элемент изолированно, а не текущее состояние списка в целом.
* * Тысяча семьдесят-семь т.е.
newList = [i for i in oldList if testFunc(i)]
Но что, если результат testFunc
зависит от элементов, уже добавленных в newList
? Или элементы все еще в oldList
, которые могут быть добавлены далее? Возможно, еще есть способ использовать понимание списка, но оно начнет терять свою элегантность, и для меня будет проще изменить список на месте.
Приведенный ниже код является одним из примеров алгоритма, который страдает от вышеуказанной проблемы. Алгоритм сократит список, так что ни один элемент не будет кратен любому другому элементу.
randInts = [70, 20, 61, 80, 54, 18, 7, 18, 55, 9]
fRandInts = FluidIterable(randInts)
fRandIntsIter = fRandInts.__iter__()
# for each value in the list (outer loop)
# test against every other value in the list (inner loop)
for i in fRandIntsIter:
print(' ')
print('outer val: ', i)
innerIntsIter = fRandInts.__iter__()
for j in innerIntsIter:
innerIndex = innerIntsIter.currentIndex
# skip the element that the outloop is currently on
# because we don't want to test a value against itself
if not innerIndex == fRandIntsIter.currentIndex:
# if the test element, j, is a multiple
# of the reference element, i, then remove 'j'
if j%i == 0:
print('remove val: ', j)
# remove element in place, without breaking the
# iteration of either loop
del fRandInts[innerIndex]
# end if multiple, then remove
# end if not the same value as outer loop
# end inner loop
# end outerloop
print('')
print('final list: ', randInts)
Вывод и окончательный сокращенный список показаны ниже
outer val: 70
outer val: 20
remove val: 80
outer val: 61
outer val: 54
outer val: 18
remove val: 54
remove val: 18
outer val: 7
remove val: 70
outer val: 55
outer val: 9
remove val: 18
final list: [20, 61, 7, 55, 9]