Я перебираю список кортежей в Python и пытаюсь удалить их, если они соответствуют определенным критериям.
for tup in somelist:
if determine(tup):
code_to_remove_tup
Что я должен использовать вместо code_to_remove_tup
? Я не могу понять, как удалить предмет таким способом.
Ответы:
Вы можете использовать понимание списка, чтобы создать новый список, содержащий только те элементы, которые вы не хотите удалять:
Или, назначив фрагменту
somelist[:]
, вы можете изменить существующий список, чтобы он содержал только те элементы, которые вы хотите:Этот подход может быть полезен, если есть другие ссылки,
somelist
которые должны отражать изменения.Вместо понимания вы также можете использовать
itertools
. В Python 2:Или в Python 3:
Для ясности и для тех, кто находит использование
[:]
обозначений хакерским или нечетким, вот более явная альтернатива. Теоретически, он должен выполнять то же самое в отношении пространства и времени, чем указанные выше строки.Он также работает на других языках, которые могут не иметь возможности замены элементов в списках Python, с минимальными изменениями. Например, не все языки приводят пустые списки к a,
False
как это делает Python. Вы можете заменитьwhile somelist:
что-то более явное, какwhile len(somelist) > 0:
.источник
somelist[:] = (x for x in somelist if determine(x))
это, чтобы создать генератор, который не может создавать ненужные копии.list_ass_slice()
функция, которая реализуетsomelist[:]=
вызовыPySequence_Fast()
внутри. Эта функция всегда возвращает список, т. Е. Решение @Alex Martelli, которое уже использует список вместо генератора, скорее всего, более эффективноsomelist
будет видоизменяться в обоих методах?Ответы, предполагающие понимание списка, почти верны - за исключением того, что они строят совершенно новый список и затем дают ему то же имя, что и старый список, они НЕ изменяют старый список на месте. Это отличается от того, что вы делаете при выборочном удалении, как в предложении @ Lennart - это быстрее, но если к вашему списку обращаются по нескольким ссылкам, тот факт, что вы просто повторно устанавливаете одну из ссылок и НЕ изменяете объект списка само по себе может привести к тонким, катастрофическим ошибкам.
К счастью, очень легко получить как скорость понимания списка, так и требуемую семантику изменения на месте - просто код:
Обратите внимание на небольшую разницу с другими ответами: этот НЕ присваивается пустому имени - он присваивает фрагменту списка, который просто является целым списком, тем самым заменяя содержимое списка в том же объекте списка Python , а не просто перезаписывая одну ссылку (из предыдущего объекта списка в новый объект списка), как и другие ответы.
источник
a
содержимым dictb
, используйтеa.clear(); a.update(b)
.x = ['foo','bar','baz']; y = x; x = [item for item in x if determine(item)];
Это переназначаетx
результат понимания списка, ноy
все еще ссылается на исходный список['foo','bar','baz']
. Если вы ожидалиx
иy
сослаться на тот же список, возможно, вы ввели ошибки. Вы это предотвратить путем присвоения ломтика всего списка, как Алекс показывает, и я показываю здесь:x = ["foo","bar","baz"]; y = x; x[:] = [item for item in x if determine(item)];
. Список изменен на месте. обеспечение того, чтобы все ссылки на список (какx
иy
здесь) ссылались на новый список.filter
функции тоже создает новый список, не изменяет элементы на месте ... толькоolist[:] = [i for i in olist if not dislike(i)]
Вам нужно сначала взять копию списка и повторить его, иначе итерация не удастся, что может привести к неожиданным результатам.
Например (зависит от того, какой тип списка):
Пример:
источник
list(somelist)
преобразует итерируемый в список.somelist[:]
делает копию объекта, который поддерживает нарезку. Таким образом, они не обязательно делают то же самое. В этом случае я хочу сделать копиюsomelist
объекта, поэтому я использую[:]
remove()
должен пройти через ВЕСЬ список для каждой итерации, так что это займет вечность.Тебе нужно идти задом наперед, иначе это похоже на спиливание ветки дерева, на котором ты сидишь :-)
Пользователи Python 2: замените
range
,xrange
чтобы избежать создания жестко закодированного спискаисточник
reversed()
встроенной функцииenumerate
возвращает итератор иreversed
ожидает последовательность. Я думаю, вы могли бы сделать,reversed(list(enumerate(somelist)))
если вы не против создать дополнительный список в памяти.m
разы медленнее.Официальное руководство по Python 2 4.2. "для заявлений"
https://docs.python.org/2/tutorial/controlflow.html#for-statements
Эта часть документов дает понять, что:
[:]
Документация по Python 2 7.3. "За заявление"
https://docs.python.org/2/reference/compound_stmts.html#for
Эта часть документов еще раз говорит о том, что вы должны сделать копию, и приводит фактический пример удаления:
Однако я не согласен с этой реализацией, поскольку
.remove()
приходится перебирать весь список, чтобы найти значение.Лучшие обходные пути
Или:
начать новый массив с нуля и
.append()
вернуться в конец: https://stackoverflow.com/a/1207460/895245Это экономит время, но экономит меньше места, поскольку сохраняет копию массива во время итерации.
использовать
del
с индексом: https://stackoverflow.com/a/1207485/895245Это более экономно, так как распределяет копию массива, но менее эффективно по времени, поскольку списки CPython реализованы с помощью динамических массивов .
Это означает, что удаление предмета требует сдвига всех следующих предметов назад на один, что является O (N).
Как правило, вы просто хотите
.append()
выбрать более быстрый вариант по умолчанию, если только память не представляет большой проблемы.Мог ли Python сделать это лучше?
Похоже, что этот конкретный Python API может быть улучшен. Сравните это, например, с:
std::vector::erase
который возвращает действительный интегратор элемента после того, как тот удалени то, и другое ясно показывает, что вы не можете изменять итерируемый список, кроме как с помощью самого итератора, и дает вам эффективные способы сделать это без копирования списка.
Возможно, основное обоснование заключается в том, что списки Python предполагаются с поддержкой динамического массива, и, следовательно, любой тип удаления будет неэффективным по времени в любом случае, в то время как Java имеет более приятную иерархию интерфейса с обеими реализациями
ArrayList
иLinkedList
реализациямиListIterator
.Похоже, в Python stdlib нет явного связанного типа списка: Python Linked List
источник
Лучшим подходом для такого примера будет понимание списка
В тех случаях, когда вы делаете что-то более сложное, чем вызов
determine
функции, я предпочитаю создавать новый список и просто добавлять его по мере необходимости. НапримерКопирование списка с использованием
remove
может сделать ваш код немного чище, как описано в одном из ответов ниже. Вы определенно не должны делать это для очень больших списков, поскольку это включает в себя сначала копирование всего списка, а также выполнениеO(n)
remove
операции для каждого удаляемого элемента, что делает этоO(n^2)
алгоритмом.источник
Для тех, кто любит функциональное программирование:
или
источник
filter
и Pythonic. 2. Если вам нужноlambda
использоватьmap
илиfilter
, список comp или genexpr всегда лучший вариант;map
иfilter
может быть немного быстрее, когда функция преобразования / предиката является встроенной в C Python, реализованной в C, и итерация не является ничтожно малой, но они всегда медленнее, когда вам нужно,lambda
что listcomp / genexpr может избежать.Мне нужно было сделать это с огромным списком, и дублирование списка казалось дорогим, тем более что в моем случае количество удалений было бы небольшим по сравнению с оставшимися элементами. Я выбрал этот низкоуровневый подход.
Что я не знаю, так это то, насколько эффективна пара удалений по сравнению с копированием большого списка. Пожалуйста, прокомментируйте, если у вас есть понимание.
источник
list
структуры данных в первую очередь должен быть тщательно продуман, поскольку удаление из середины списка занимает линейное время по длине списка. Если вам действительно не нужен произвольный доступ к k-му последовательному элементу, возможно, подумайтеOrderedDict
?newlist = []
, а потомnewlist.append(array[i])
просто раньшеdel array[i]
?list()
это связанный список, произвольный доступ дорог, еслиlist()
массив, удаление дорого, поскольку они требуют перемещения всех следующих элементов вперед. Приличный итератор может помочь в реализации связанного списка. Это, однако, может быть эффективным с точки зрения пространства.Также было бы разумно просто создать новый список, если текущий элемент списка соответствует желаемым критериям.
так:
и чтобы избежать необходимости перекодировать весь проект с новым именем списков:
обратите внимание, из документации Python:
источник
Этот ответ был первоначально написан в ответ на вопрос, который был помечен как дубликат: удаление координат из списка на python
В вашем коде есть две проблемы:
1) При использовании remove () вы пытаетесь удалить целые числа, тогда как вам нужно удалить кортеж.
2) Цикл for пропустит элементы в вашем списке.
Давайте разберемся, что происходит, когда мы выполняем ваш код:
Первая проблема заключается в том, что вы передаете оба «a» и «b» для remove (), но remove () принимает только один аргумент. Итак, как мы можем заставить remove () правильно работать с вашим списком? Нам нужно выяснить, что представляет собой каждый элемент вашего списка. В этом случае каждый из них является кортежем. Чтобы увидеть это, давайте перейдем к одному элементу списка (индексация начинается с 0):
Ага! Каждый элемент L1 на самом деле является кортежем. Так вот что мы должны передать, чтобы удалить (). Кортежи в python очень просты, они просто создаются путем заключения значений в скобки. «a, b» не является кортежем, но «(a, b)» является кортежем. Поэтому мы модифицируем ваш код и запускаем его снова:
Этот код выполняется без ошибок, но давайте посмотрим на список, который он выводит:
Почему (1, -2) все еще в вашем списке? Оказывается, изменение списка при использовании цикла для его перебора - очень плохая идея без особой осторожности. Причина того, что (1, -2) остается в списке, заключается в том, что местоположения каждого элемента в списке менялись между итерациями цикла for. Давайте посмотрим, что произойдет, если мы добавим приведенный выше код в более длинный список:
Как вы можете сделать из этого результата, каждый раз, когда условный оператор оценивается как true и элемент списка удаляется, следующая итерация цикла будет пропускать оценку следующего элемента в списке, поскольку его значения теперь расположены с разными индексами.
Наиболее интуитивным решением является копирование списка, затем итерация по исходному списку и изменение только копии. Вы можете попробовать сделать это так:
Однако выходные данные будут идентичны предыдущим:
Это потому, что когда мы создали L2, python фактически не создавал новый объект. Вместо этого он просто ссылался на L2 на тот же объект, что и L1. Мы можем проверить это с помощью «is», которое отличается от просто «равно» (==).
Мы можем сделать настоящую копию, используя copy.copy (). Тогда все работает как положено:
Наконец, есть одно более чистое решение, чем создание совершенно новой копии L1. Обратный () функция:
К сожалению, я не могу адекватно описать, как работает reversed (). Он возвращает объект 'listreverseiterator', когда ему передается список. В практических целях вы можете думать об этом как о создании обратной копии аргумента. Это решение, которое я рекомендую.
источник
Если вы хотите сделать что-то еще во время итерации, может быть неплохо получить как индекс (который гарантирует вам возможность ссылаться на него, например, если у вас есть список диктов), так и фактическое содержимое элемента списка.
enumerate
дает вам доступ к элементу и индексу сразу.reversed
так что индексы, которые вы собираетесь удалить позже, не изменятся на вас.источник
Вы можете использовать
filter()
доступные как встроенные.Для более подробной информации проверьте здесь
источник
Большинство ответов здесь хотят, чтобы вы создали копию списка. У меня был случай использования, когда список был довольно длинным (110 КБ), и было разумнее продолжать сокращать список.
Прежде всего вам нужно заменить цикл foreach на цикл while ,
Значение
i
в блоке if не изменяется, потому что вы захотите получить значение нового элемента из того же индекса после удаления старого элемента.источник
Вы можете попробовать выполнить цикл в обратном порядке, поэтому для some_list вы будете делать что-то вроде:
Таким образом, индекс выравнивается и не страдает от обновления списка (независимо от того, используете ли вы элемент cur или нет).
источник
reversed(list(enumerate(some_list)))
будет проще, чем вычисление индексов самостоятельно.Одно из возможных решений, полезное, если вы хотите не только удалить некоторые вещи, но и сделать что-то со всеми элементами в одном цикле:
источник
bad
, что-то с этим сделать, а также что-то сделатьgood
в одном цикле?alist[:]
). И поскольку вы, возможно, делаете что-то необычное, у него действительно есть сценарий использования. Хороший пересмотр - это хорошо. Возьми мой голос.Мне нужно было сделать что-то похожее, и в моем случае проблема заключалась в памяти - мне нужно было объединить несколько объектов набора данных в списке, после того, как они поработали с ними, в новый объект, и мне нужно было избавиться от каждой записи, с которой я сливался. избегайте дублирования их всех и взрыва памяти. В моем случае наличие объектов в словаре вместо списка работало нормально:
`` `
`` `
источник
TLDR:
Я написал библиотеку, которая позволяет вам сделать это:
Лучше использовать другой метод, если это возможно, который не требует изменения вашей итерации при ее повторении, но для некоторых алгоритмов это может быть не так просто. И поэтому, если вы уверены, что действительно хотите код шаблона, описанный в исходном вопросе, это возможно.
Должен работать со всеми изменяемыми последовательностями, а не только со списками.
Полный ответ:
Редактировать: последний пример кода в этом ответе дает пример использования того, почему вы можете захотеть изменить список на месте, а не использовать его для понимания. Первая часть ответов служит руководством к тому, как можно изменить массив на месте.
Решение следует из этого ответа (для связанного вопроса) от senderle. Что объясняет, как индекс массива обновляется при переборе списка, который был изменен. Приведенное ниже решение предназначено для правильного отслеживания индекса массива, даже если список изменен.
Скачать
fluidIter.py
с здесьhttps://github.com/alanbacon/FluidIterator
, это просто один файл поэтому нет необходимости устанавливать мерзавца. Установщика не существует, поэтому вам нужно убедиться, что файл находится в пути python. Код был написан для Python 3 и не тестировался на Python 2.Это даст следующий результат:
Выше мы использовали
pop
метод объекта списка флюидов. Другие общие Iterable также реализованы методы , такие какdel fluidL[i]
,.remove
,.insert
,.append
,.extend
. Список также можно изменить с помощью срезов (sort
иreverse
методы не реализованы).Единственным условием является то, что вы должны только изменить список на месте, если в какой-то момент
fluidL
или если онl
был переназначен на другой объект списка, код не будет работать. ИсходныйfluidL
объект по-прежнему будет использоваться циклом for, но мы не сможем его изменить.т.е.
Если мы хотим получить доступ к текущему значению индекса списка, мы не можем использовать перечисление, поскольку это только подсчитывает, сколько раз цикл for выполнялся. Вместо этого мы будем использовать объект итератора напрямую.
Это выведет следующее:
FluidIterable
Класс просто предоставляет обертку для исходного объекта списка. Доступ к исходному объекту можно получить как свойство объекта Fluid, например:Больше примеров / тестов можно найти в
if __name__ is "__main__":
разделе внизуfluidIter.py
. На них стоит посмотреть, потому что они объясняют, что происходит в различных ситуациях. Например: замена больших разделов списка с использованием фрагмента. Или используя (и модифицируя) ту же итерацию во вложенных циклах for.Как я уже сказал, для начала: это сложное решение, которое ухудшит читабельность вашего кода и затруднит его отладку. Поэтому другие решения , такие как списковые упомянутый в Дэвиде Raznick в ответе следует считать первым. При этом я нашел случаи, когда этот класс был полезен для меня и его было проще использовать, чем отслеживать индексы элементов, которые необходимо удалить.
Изменить: как уже упоминалось в комментариях, этот ответ на самом деле не представляет проблему, для которой этот подход обеспечивает решение. Я постараюсь рассмотреть это здесь:
Понимание списка обеспечивает способ создания нового списка, но эти подходы имеют тенденцию рассматривать каждый элемент изолированно, а не текущее состояние списка в целом.
т.е.
Но что, если результат
testFunc
зависит от уже добавленных элементовnewList
? Или элементы все еще вoldList
этом, могут быть добавлены далее? Может все же быть способ использовать понимание списка, но он начнет терять свою элегантность, и для меня легче изменить список на месте.Приведенный ниже код является одним из примеров алгоритма, который страдает от вышеуказанной проблемы. Алгоритм сократит список, так что ни один элемент не будет кратным любому другому элементу.
Результат и окончательный сокращенный список показаны ниже
источник
some_list[:] = [x for x in some_list if not some_condition(x)]
не достигается? Без ответа на этот вопрос, почему кто-то должен верить, что загрузка и использование вашей 600-строчной библиотеки с опечатками и закомментированным кодом является лучшим решением их проблемы, чем однострочная? -1.some_list[:] = [x for x in some_list if not some_condition(y)]
гдеy
находится другой элемент спискаx
. И не было бы возможности написатьsome_list[:] = [x for x in some_list if not some_condition(intermediateStateOf_some_list)]
.Самый эффективный метод - это понимание списка, многие люди показывают свое дело, конечно, это также хороший способ справиться с
iterator
ситуациейfilter
.Вот пример (получите шансы в кортеже):
Внимание: вы также не можете обрабатывать итераторы. Итераторы иногда лучше, чем последовательности.
источник
цикл for будет проходить через индекс.
считай, у тебя есть список,
вы используете переменную списка с именем
lis
. и вы используете то же самое, чтобы удалить ..ваша переменная
во время 5-й итерации,
Ваш номер 35 не был простым, поэтому вы удалили его из списка.
а затем следующее значение (65) перейти к предыдущему индексу.
так что 4-я итерация сделала указатель переместился на 5-ую ..
вот почему ваш цикл не покрывает 65, так как он переместился в предыдущий индекс.
поэтому вы не должны ссылаться на список в другой переменной, которая все еще ссылается на оригинал, а не на копию.
поэтому сделайте копию списка, используя
list[::]
теперь тебе это даст,
Проблема в том, что вы удалили значение из списка во время итерации, после чего индекс вашего списка свернется.
так что вы можете попробовать понимание вместо этого.
который поддерживает все повторяемые как, список, кортеж, dict, строка и т. д.
источник
Если вы хотите удалить элементы из списка во время итерации, используйте цикл while, чтобы вы могли изменять текущий индекс и индекс конца после каждого удаления.
Пример:
источник
Остальные ответы верны: обычно плохая идея удалять из списка, который вы перебираете. Обратная итерация позволяет избежать ловушек, но гораздо сложнее следовать коду, который делает это, поэтому обычно вам лучше использовать понимание списка или
filter
.Однако есть один случай, когда безопасно удалить элементы из последовательности, которую вы повторяете: если вы удаляете только один элемент во время итерации. Это может быть обеспечено с помощью
return
илиbreak
. Например:Это часто легче понять, чем понимание списка, когда вы выполняете некоторые операции с побочными эффектами для первого элемента в списке, который удовлетворяет некоторому условию, а затем сразу же удаляете этот элемент из списка.
источник
Я могу придумать три подхода для решения вашей проблемы. В качестве примера я создам случайный список кортежей
somelist = [(1,2,3), (4,5,6), (3,6,6), (7,8,9), (15,0,0), (10,11,12)]
. Условие, которое я выбираю, таковоsum of elements of a tuple = 15
. В финальном списке у нас будут только те кортежи, чья сумма не равна 15.То, что я выбрал, является случайно выбранным примером. Вы можете изменить в список кортежей и условие , что я выбрал.
Метод 1.> Используйте предложенный вами фреймворк (где заполняется код внутри цикла for). Я использую небольшой код
del
для удаления кортежа, который удовлетворяет указанному условию. Однако этот метод пропускает кортеж (который удовлетворяет указанному условию), если два последовательно размещенных кортежа удовлетворяют данному условию.Метод 2.> Создайте новый список, который содержит элементы (кортежи), где данное условие не выполняется (это то же самое, что удаление элементов списка, где выполняется данное условие). Ниже приведен код для этого:
Метод 3.> Найдите индексы, в которых выполняется данное условие, а затем используйте элементы удаления (кортежи), соответствующие этим индексам. Ниже приведен код для этого.
Метод 1 и метод 2 быстрее, чем метод 3 . Метод2 и метод3 более эффективны, чем метод1. Я предпочитаю method2 . Для вышеупомянутого примера
time(method1) : time(method2) : time(method3) = 1 : 1 : 1.7
источник
Для всего, что может быть действительно большим, я использую следующее.
Это должно быть значительно быстрее, чем все остальное.
источник
В некоторых ситуациях, когда вы делаете больше, чем просто фильтруете список по одному элементу за раз, вы хотите, чтобы ваша итерация изменялась во время итерации.
Вот пример, где предварительное копирование списка некорректно, обратная итерация невозможна, и понимание списка также не вариант.
источник
Если вы будете использовать новый список позже, вы можете просто установить элемент в None, а затем судить о нем в последующем цикле, как это
Таким образом, вам не нужно копировать список, и это легче понять.
источник
открыть список чисел, и вы хотите удалить все не делятся на 3,
используя
list comprehension
, это создаст новый список и создаст новое пространство памятииспользуя
lambda filter
функцию, это создаст результирующий новый список и займет место памятибез использования места в памяти для нового списка и изменения существующего списка
источник