Подтвердить что ты не робот

Как удалить элемент в списке, если он существует?

Я получаю new_tag из текстового поля формы с self.response.get("new_tag") и selected_tags из полей флажка с помощью

self.response.get_all("selected_tags")

Я объединяю их следующим образом:

tag_string = new_tag
new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)

(f1.striplist - это функция, которая перекрывает пробелы внутри строк в списке.)

Но в случае, когда tag_list пуст (новые теги не введены), но есть некоторые selected_tags, new_tag_list содержит пустую строку " ".

Например, из logging.info:

new_tag
selected_tags[u'Hello', u'Cool', u'Glam']
new_tag_list[u'', u'Hello', u'Cool', u'Glam']

Как избавиться от пустой строки?

Если в списке есть пустая строка:

>>> s = [u'', u'Hello', u'Cool', u'Glam']
>>> i = s.index("")
>>> del s[i]
>>> s
[u'Hello', u'Cool', u'Glam']

Но если пустой строки нет:

>>> s = [u'Hello', u'Cool', u'Glam']
>>> if s.index(""):
        i = s.index("")
        del s[i]
    else:
        print "new_tag_list has no empty string"

Но это дает:

Traceback (most recent call last):
  File "<pyshell#30>", line 1, in <module>
    if new_tag_list.index(""):
        ValueError: list.index(x): x not in list

Почему это происходит, и как мне его обойти?

4b9b3361

Ответ 1

1) Почти английский стиль:

Проверьте наличие с помощью оператора in, затем примените метод remove.

if thing in some_list: some_list.remove(thing)

Метод remove удалит только первое вхождение thing, чтобы удалить все вхождения, вы можете использовать while вместо if.

while thing in some_list: some_list.remove(thing)    
  • Достаточно простой, возможно, мой выбор. Для небольших списков (не может устоять перед одним слоем)

2) Duck-typed, EAFP стиль:

Это первое в Python обращение с первым спросом-вопросом. Вместо проверки заранее, если объект подходит, просто выполните операцию и уловите соответствующие Исключения:

try:
    some_list.remove(thing)
except ValueError:
    pass # or scream: thing not in some_list!
except AttributeError:
    call_security("some_list not quacking like a list!")

В то время как вторая исключающая фраза в приведенном выше примере относится не только к сомнительному юмору, но и совершенно ненужной (необходимо было продемонстрировать утиную печать для людей, не знакомых с концепцией).

Если вы ожидаете множественные вхождения вещи:

while True:
    try:
        some_list.remove(thing)
    except ValueError:
        break
  • немного подробный для этого конкретного варианта использования, но очень идиоматический в Python.
  • это работает лучше, чем # 1
  • PEP 463 предложил более короткий синтаксис для try/except simple usage, который был бы здесь полезен, но он не был одобрен.

Однако с contextlib suppress() contextmanager (введенный в python 3.4) приведенный выше код может быть упрощен:

with suppress(ValueError, AttributeError):
    some_list.remove(thing)

Опять же, если вы ожидаете множественные вхождения вещи:

with suppress(ValueError):
    while True:
        some_list.remove(thing)

3) Функциональный стиль:

Примерно в 1993 году Python получил lambda, reduce(), filter() и map(), любезно предоставленный Lisp хакер которые пропустили их и представили рабочие патчи *. Вы можете использовать filter для удаления элементов из списка:

is_not_thing = lambda x: x is not thing
cleaned_list = filter(is_not_thing, some_list)

Существует ярлык, который может быть полезен для вашего случая: если вы хотите отфильтровать пустые элементы (на самом деле элементы, где bool(item) == False, например None, нуль, пустые строки или другие пустые коллекции), вы можете пройти Нет как первый аргумент:

cleaned_list = filter(None, some_list)
  • [обновление]: в Python 2.x, filter(function, iterable) используется эквивалент [item for item in iterable if function(item)] (или [item for item in iterable if item], если первый аргумент None); в Python 3.x теперь он эквивалентен (item for item in iterable if function(item)). Тонкая разница заключается в том, что фильтр используется для возврата списка, теперь он работает как выражение генератора - это нормально, если вы только итерируете по очищенному списку и отбрасываете его, но если вам действительно нужен список, вы должны заключить filter() с помощью конструктора list().
  • * Эти Lispy ароматизированные конструкции считаются немного чуждыми в Python. Примерно в 2005 году Гвидо даже говорил о снижении filter - вместе со спутниками map и reduce (они еще не ушли, но reduce был перемещен в модуль functools, который стоит посмотреть, если вам нравится функции высокого порядка).

4) Математический стиль:

List comprehensions стал предпочтительным стилем для манипулирования списками в Python, поскольку введен в версии 2.0 с помощью PEP 202. Обоснованием этого является то, что представления List обеспечивают более сжатый способ создания списков в ситуациях, когда в настоящее время используются map() и filter() и/или вложенные петли.

cleaned_list = [ x for x in some_list if x is not thing ]

Выражения генератора были введены в версии 2.4 с помощью PEP 289. Выражение генератора лучше для ситуаций, когда вам действительно не нужен (или нужен) полный список, созданный в памяти, например, когда вы просто хотите перебирать элементы по одному. Если вы только перебираете список, вы можете представить выражение генератора как ленивое оценивание:

for item in (x for x in some_list if x is not thing):
    do_your_thing_with(item)

Примечания

  • вы можете использовать оператор неравенства != вместо is not (разница важна)
  • для критиков методов, подразумевающих копию списка: вопреки распространенному мнению, выражения генератора не всегда более эффективны, чем понимание списков - пожалуйста, профайл, прежде чем жаловаться

Ответ 2

try:
    s.remove("")
except ValueError:
    print "new_tag_list has no empty string"

Обратите внимание, что это удалит только один экземпляр пустой строки из вашего списка (как и ваш код). Может ли ваш список содержать более одного?

Ответ 3

Если index не находит искомую строку, она выдает ValueError, который вы видите. Или catch ValueError:

try:
    i = s.index("")
    del s[i]
except ValueError:
    print "new_tag_list has no empty string"

или используйте find, который в этом случае возвращает -1.

i = s.find("")
if i >= 0:
    del s[i]
else:
    print "new_tag_list has no empty string"

Ответ 4

Eek, не делайте ничего сложного:)

Просто filter() ваши теги. bool() возвращает False для пустых строк, поэтому вместо

new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)

вам следует написать

new_tag_list = filter(bool, f1.striplist(tag_string.split(",") + selected_tags))

или еще лучше, поместите эту логику внутри striplist(), чтобы она не возвращала пустые строки в первую очередь.

Ответ 5

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

Если у вас очень большие списки, удаление из конца списка позволяет избежать внутренних элементов CPython, имеющих memmove, для ситуаций, когда вы можете переупорядочить список. Это дает усиление производительности для удаления из конца списка, так как ему не нужно memmove каждого элемента после того, как вы удаляете один шаг (1).
Для одноразовых абстракций разница в производительности может быть приемлемой, но если у вас большой список и вам нужно удалить много элементов - вы, вероятно, заметите поражение в производительности.

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

Этот метод может быть использован для более эффективного удаления, до тех пор, пока переупорядочивание списка допустимо. (2)

def remove_unordered(ls, item):
    i = ls.index(item)
    ls[-1], ls[i] = ls[i], ls[-1]
    ls.pop()

Возможно, вы захотите избежать возникновения ошибки, если item отсутствует в списке.

def remove_unordered_test(ls, item):
    try:
        i = ls.index(item)
    except ValueError:
        return False
    ls[-1], ls[i] = ls[i], ls[-1]
    ls.pop()
    return True

  • Пока я тестировал это с помощью CPython, его вполне вероятно, что большинство/все другие реализации Python используют массив для хранения списков внутри. Поэтому, если они не используют сложную структуру данных, предназначенную для эффективного перераспределения списка, они, вероятно, имеют одинаковую характеристику производительности.

Простой способ проверить это, сравните разницу в скорости с удалением из передней части списка с удалением последнего элемента:

python -m timeit 'a = [0] * 100000' 'while a: a.remove(0)'

С

python -m timeit 'a = [0] * 100000' 'while a: a.pop()'

(дает порядок разницы скоростей, где второй пример быстрее с CPython и PyPy).

  1. В этом случае вы можете использовать set, особенно если список не предназначен для хранения дубликатов. На практике, хотя вам может потребоваться сохранить изменяемые данные, которые нельзя добавить в set. Также проверьте btree, если данные можно заказать.

Ответ 6

Вот еще один подход к одному слою:

next((some_list.pop(i) for i, l in enumerate(some_list) if l == thing), None)

Он не создает копию списка, не делает несколько проходов по списку, не требует дополнительной обработки исключений и возвращает согласованный объект или None, если нет совпадения. Только проблема заключается в том, что он делает длинный оператор.

В общем, при поиске однострочного решения, которое не генерирует исключений, next() - это путь, поскольку он является одной из немногих функций Python, поддерживающих аргумент по умолчанию.

Ответ 7

Все, что вам нужно сделать, это

list = ["a", "b", "c"]
    try:
        list.remove("a")
    except:
        print("meow")

но этот метод имеет проблему. Вы должны положить что-то в заблуждение поэтому я нашел это:

list = ["a", "b", "c"]
if "a" in str(list):
    list.remove("a")