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

Python: удалить словарь из списка

Если у меня есть список словарей, скажите:

[{'id': 1, 'name': 'paul'},
{'id': 2, 'name': 'john'}]

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

4b9b3361

Ответ 1

thelist[:] = [d for d in thelist if d.get('id') != 2]

Изменить: поскольку некоторые сомнения были высказаны в комментарии о производительности этого кода (некоторые из них основаны на непонимании характеристик производительности Python, некоторые из которых предполагают, что за пределами указанных спецификаций существует ровно один dict в список со значением 2 для ключевого "id" ), я хочу предложить заверения в этой точке.

В старом ящике Linux, измеряющем этот код:

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(99)]; import random" "thelist=list(lod); random.shuffle(thelist); thelist[:] = [d for d in thelist if d.get('id') != 2]"
10000 loops, best of 3: 82.3 usec per loop

из которых около 57 микросекунд для random.shuffle(необходимо для того, чтобы удалить элемент не ВСЕГДА в том же месте;-) и 0,65 мкс для начальной копии (кто бы ни беспокоился о влиянии производительности мелких копий Python списки наиболее очевидно для обеда;-), необходимо избегать изменения исходного списка в цикле (так что каждая нога цикла имеет что-то удалить;).

Когда известно, что удалить только один элемент, его можно найти и удалить еще быстрее:

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(99)]; import random" "thelist=list(lod); random.shuffle(thelist); where=(i for i,d in enumerate(thelist) if d.get('id')==2).next(); del thelist[where]"
10000 loops, best of 3: 72.8 usec per loop

(используйте next встроенный, а не метод .next, если вы на Python 2.6 или лучше, конечно) - но этот код ломается, если число dicts, удовлетворяющих условию удаления, не точно один. Обобщая это, мы имеем:

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*3; import random" "thelist=list(lod); where=[i for i,d in enumerate(thelist) if d.get('id')==2]; where.reverse()" "for i in where: del thelist[i]"
10000 loops, best of 3: 23.7 usec per loop

где перетасовка может быть удалена, поскольку, как мы знаем, уже существует три равных dicts для удаления. И listcomp, неизменный, тарифы хорошо:

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*3; import random" "thelist=list(lod); thelist[:] = [d for d in thelist if d.get('id') != 2]"
10000 loops, best of 3: 23.8 usec per loop

полностью шея и шея, причем даже 3 элемента из 99 должны быть удалены. С более длинными списками и большим количеством повторений это еще более важно:

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*133; import random" "thelist=list(lod); where=[i for i,d in enumerate(thelist) if d.get('id')==2]; where.reverse()" "for i in where: del thelist[i]"
1000 loops, best of 3: 1.11 msec per loop
$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*133; import random" "thelist=list(lod); thelist[:] = [d for d in thelist if d.get('id') != 2]"
1000 loops, best of 3: 998 usec per loop

В целом, очевидно, не стоит развертывать тонкость создания и изменения списка индексов для удаления, а также простое и очевидное понимание списка, чтобы, возможно, получить 100 наносекунд в одном маленьком случае - и потерять 113 микросекунд в более крупный;-). Избегание или критика простых, простых и безупречно эффективных решений (например, понимание списков для этого общего класса "удалить некоторые элементы из списка" ) - особенно неприятный пример известного принципа Кнут и Хоар: "преждевременная оптимизация корень всего зла в программировании"! -)

Ответ 2

Здесь можно сделать это со списком (при условии, что вы назовете свой список "foo" ):

[x for x in foo if not (2 == x.get('id'))]

Подставьте 'john' == x.get('name') или что угодно, если необходимо.

filter также работает:

foo.filter(lambda x: x.get('id')!=2, foo)

И если вы хотите генератор, вы можете использовать itertools:

itertools.ifilter(lambda x: x.get('id')!=2, foo)

Однако, как и в случае с Python 3, filter все равно вернет итератор, поэтому понимание списка действительно лучший выбор, как предложил Алекс.

Ответ 3

Это не ответ, так как я думаю, что вам уже предложили хорошие, но вы рассматривали возможность использования словаря <id>:<name> вместо списка словарей?

Ответ 4

# assume ls contains your list
for i in range(len(ls)):
    if ls[i]['id'] == 2:
        del ls[i]
        break

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

Ответ 5

Вы можете попробовать следующее:

a = [{'id': 1, 'name': 'paul'},
     {'id': 2, 'name': 'john'}]

for e in range(len(a) - 1, -1, -1):
    if a[e]['id'] == 2:
        a.pop(e)

Если вы не можете постить с самого начала - поп с конца, он не испортит цикл for.

Ответ 6

list.pop() - хороший выбор:

>>> a = [{'id': 1, 'name': 'paul'},
... {'id': 2, 'name': 'john'}]
>>> a.pop(1)
{'id': 2, 'name': 'john'}
>>> a
[{'id': 1, 'name': 'paul'}]

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

>>> a = [{'id': 1, 'name': 'paul'},
... {'id': 2, 'name': 'john'}]
>>> for i in reversed(range(len(a))):
...     if a[i].get('id') == 2:
...             a.pop(i)
...
{'id': 2, 'name': 'john'}
>>> a
[{'id': 1, 'name': 'paul'}]

Другая возможность - использовать del:

[{'id': 1, 'name': 'paul'}]
>>> a = [{'id': 1, 'name': 'paul'},
... {'id': 2, 'name': 'john'}]
>>> a
[{'id': 1, 'name': 'paul'}, {'id': 2, 'name': 'john'}]
>>> del a[1]
>>> a
[{'id': 1, 'name': 'paul'}]

Ответ 7

Вы можете попробовать что-то в следующих строках:

def destructively_remove_if(predicate, list):
      for k in xrange(len(list)):
          if predicate(list[k]):
              del list[k]
              break
      return list

  list = [
      { 'id': 1, 'name': 'John' },
      { 'id': 2, 'name': 'Karl' },
      { 'id': 3, 'name': 'Desdemona' } 
  ]

  print "Before:", list
  destructively_remove_if(lambda p: p["id"] == 2, list)
  print "After:", list

Если вы не создадите что-то похожее на индекс над вашими данными, я не думайте, что вы можете сделать лучше, чем сделать таблицу грубой силы сканировать "по всему списку. Если ваши данные отсортированы по ключу, вы вы можете использовать модуль bisect для найдите объект, который вы ищете, несколько быстрее.

Ответ 8

Предположим, что ваша версия Python 3.6 или выше, и что вам не нужен удаленный элемент, это будет дешевле...

Если словари в списке уникальны:

for i in range(len(dicts)):
    if dicts[i].get('id') == 2:
        del dicts[i]
        break

Если вы хотите удалить все подходящие элементы:

for i in range(len(dicts)):
    if dicts[i].get('id') == 2:
        del dicts[i]

Вы также можете быть уверены, что получение ключа id не вызовет keyerror независимо от версии Python.

if dicts [i].get('id', None) == 2