Python - проверьте, существует ли ключ в dict с большим количеством подзадач [дубликат]

Я предпочитаю, чтобы прослушиватели событий были развернуты модульной функцией, а не сценарием прослушивателя событий уровня document. Итак, мне нравится ниже. Обратите внимание: вы не можете переадресовать элемент с одним и тем же прослушивателем событий, поэтому не беспокойтесь о прикреплении слушателя более одного раза - только один палец.

var iterations = 4;
var button;
var body = document.querySelector("body");

for (var i = 0; i < iterations; i++) {
    button = document.createElement("button");
    button.classList.add("my-button");
    button.appendChild(document.createTextNode(i));
    button.addEventListener("click", myButtonWasClicked);
    body.appendChild(button);
}

function myButtonWasClicked(e) {
    console.log(e.target); //access to this specific button
}

43
задан Cœur 15 April 2017 в 15:33
поделиться

8 ответов

Я нашел этот Q / A очень интересным, так как он предлагает несколько разных решений для одной и той же проблемы. Я взял все эти функции и протестировал их со сложным словарным объектом. Я должен был взять две функции из теста, потому что им приходилось много неудачных результатов, и они не поддерживали возвращаемые списки или dicts как значения, которые я считаю существенными, так как функция должна быть подготовлена ​​почти для any

Итак, я перекачал другие функции в 100 000 итераций через модуль timeit, и выход пришел к следующему результату:

0.11 usec/pass on gen_dict_extract(k,o)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6.03 usec/pass on find_all_items(k,o)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0.15 usec/pass on findkeys(k,o)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1.79 usec/pass on get_recursively(k,o)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0.14 usec/pass on find(k,o)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0.36 usec/pass on dict_extract(k,o)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Все функции имели одинаковую иглу для поиска («logging») и того же словарного объекта, который строится следующим образом:

o = { 'temparature': '50', 
      'logging': {
        'handlers': {
          'console': {
            'formatter': 'simple', 
            'class': 'logging.StreamHandler', 
            'stream': 'ext://sys.stdout', 
            'level': 'DEBUG'
          }
        },
        'loggers': {
          'simpleExample': {
            'handlers': ['console'], 
            'propagate': 'no', 
            'level': 'INFO'
          },
         'root': {
           'handlers': ['console'], 
           'level': 'DEBUG'
         }
       }, 
       'version': '1', 
       'formatters': {
         'simple': {
           'datefmt': "'%Y-%m-%d %H:%M:%S'", 
           'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
         }
       }
     }, 
     'treatment': {'second': 5, 'last': 4, 'first': 4},   
     'treatment_plan': [[4, 5, 4], [4, 5, 4], [5, 5, 5]]
}

Все функции выдают тот же результат, но разницы во времени являются драматическими! Функция gen_dict_extract(k,o) - это моя функция, адаптированная из функций здесь, на самом деле она во многом похожа на функцию find от Alfe, с основным отличием, что я проверяю, имеет ли данный объект функцию iteritems, в случае передачи строк во время рекурсии:

def gen_dict_extract(key, var):
    if hasattr(var,'iteritems'):
        for k, v in var.iteritems():
            if k == key:
                yield v
            if isinstance(v, dict):
                for result in gen_dict_extract(key, v):
                    yield result
            elif isinstance(v, list):
                for d in v:
                    for result in gen_dict_extract(key, d):
                        yield result

Таким образом, этот вариант является самым быстрым и безопасным из функций здесь. И find_all_items невероятно медленный и далеко от второго самого медленного get_recursivley, в то время как остальные, кроме dict_extract, близки друг к другу. Функции fun и keyHole работают только в том случае, если вы ищете строки.

Интересный аспект обучения здесь:)

32
ответ дан hexerei software 19 August 2018 в 06:57
поделиться
  • 1
    Это правильный ответ для любого, кроме узкого случая OP. Это работало над каждой неприятной штукой, которую может на нее нажимать jsonpickle . – Bruno Bronosky 20 February 2017 в 05:22
  • 2
    Если вы хотите найти несколько ключей, как я, просто: (1) измените на gen_dict_extract(keys, var) (2) положите for key in keys: как строка 2 & amp; отступ остальных (3) изменить первый выход на yield {key: v} – Bruno Bronosky 20 February 2017 в 05:22
  • 3
    Вы сравниваете яблоки с апельсинами. Запуск функции, возвращающей генератор, занимает меньше времени, чем запуск функции, которая возвращает конечный результат. Попробуйте timeit на next(functionname(k, o) для всех решений генератора. – kaleissin 18 July 2017 в 13:10
  • 4
    hasattr(var, 'items') для python3 – gobrewers14 8 September 2017 в 16:16
  • 5
    Считаете ли вы, что вы отделите if hasattr часть версии, используя try, чтобы поймать исключение в случае сбоя вызова (см. pastebin.com/ZXvVtV0g для возможной реализации)? Это уменьшило бы удвоенный поиск атрибута iteritems (один раз для hasattr() и один раз для вызова), и, следовательно, возможно, сократить время выполнения (что для вас важно). Однако не было никаких контрольных показателей. – Alfe 9 November 2017 в 10:56
def find(key, value):
  for k, v in value.iteritems():
    if k == key:
      yield v
    elif isinstance(v, dict):
      for result in find(key, v):
        yield result
    elif isinstance(v, list):
      for d in v:
        for result in find(key, d):
          yield result
11
ответ дан Alfe 19 August 2018 в 06:57
поделиться
  • 1
    Если хотите, вы можете удалить ветвь dict-elif. ваш случай, похоже, не имеет этих данных. – Alfe 21 March 2012 в 17:50
  • 2
    Это отлично работает, но также сталкивается с проблемами, если он сталкивается с списком, который содержит прямую строку (которую я забыл включить в мой пример). Я думаю, что добавление в isinstance чек для dict перед последними двумя строками решает это. – Matt Swain 21 March 2012 в 18:15
  • 3
    Спасибо за награды, но я был бы гордым, чтобы получить их за чистоту моего кода, чем за его скорость. – Alfe 16 April 2015 в 08:10
  • 4
    Да, в 95% случаев. Оставшиеся (редкие) случаи - это те, в которых некоторое ограничение времени может заставить меня выбрать более быструю версию над более чистой. Но мне это не нравится. Это всегда означает наложить нагрузку на моего преемника, который должен будет поддерживать этот код. Это риск, потому что мой преемник может запутаться. Тогда мне придется написать много комментариев, может быть, целый документ, объясняющий мои мотивы, временные эксперименты, их результаты и т. Д. Это помогает мне и всем коллегам сделать это правильно. Очиститель проще. – Alfe 5 December 2016 в 15:37
  • 5
    @Alfe - спасибо за этот ответ. Мне нужно было извлечь все вхождения строки во вложенном dict для конкретного примера использования Elasticsearch, и этот код был полезен с незначительной модификацией - stackoverflow.com/questions/40586020/… – Saurabh Hirani 5 August 2018 в 06:48
d = { "id" : "abcde",
    "key1" : "blah",
    "key2" : "blah blah",
    "nestedlist" : [
    { "id" : "qwerty",
        "nestednestedlist" : [
        { "id" : "xyz", "keyA" : "blah blah blah" },
        { "id" : "fghi", "keyZ" : "blah blah blah" }],
        "anothernestednestedlist" : [
        { "id" : "asdf", "keyQ" : "blah blah" },
        { "id" : "yuiop", "keyW" : "blah" }] } ] }


def findkeys(node, kv):
    if isinstance(node, list):
        for i in node:
            for x in findkeys(i, kv):
               yield x
    elif isinstance(node, dict):
        if kv in node:
            yield node[kv]
        for j in node.values():
            for x in findkeys(j, kv):
                yield x

print list(findkeys(d, 'id'))
3
ответ дан arainchi 19 August 2018 в 06:57
поделиться

Вот мой удар по нему:

def keyHole(k2b,o):
  # print "Checking for %s in "%k2b,o
  if isinstance(o, dict):
    for k, v in o.iteritems():
      if k == k2b and not hasattr(v, '__iter__'): yield v
      else:
        for r in  keyHole(k2b,v): yield r
  elif hasattr(o, '__iter__'):
    for r in [ keyHole(k2b,i) for i in o ]:
      for r2 in r: yield r2
  return

Пример:

>>> findMe = {'Me':{'a':2,'Me':'bop'},'z':{'Me':4}}
>>> keyHole('Me',findMe)
<generator object keyHole at 0x105eccb90>
>>> [ x for x in keyHole('Me',findMe) ]
['bop', 4]
0
ответ дан AXE-Labs 19 August 2018 в 06:57
поделиться

Вот как я это сделал.

Эта функция рекурсивно ищет словарь, содержащий вложенные словари и списки. Он создает список с именем fields_found, который содержит значение для каждого момента поиска поля. «Поле» - это ключ, который я ищу в словаре и его вложенных списках и словарях.

def get_recursively(search_dict, field):
    """Takes a dict with nested lists and dicts,
    and searches all dicts for a key of the field
    provided.
    """
    fields_found = []

    for key, value in search_dict.iteritems():

        if key == field:
            fields_found.append(value)

        elif isinstance(value, dict):
            results = get_recursively(value, field)
            for result in results:
                fields_found.append(result)

        elif isinstance(value, list):
            for item in value:
                if isinstance(item, dict):
                    more_results = get_recursively(item, field)
                    for another_result in more_results:
                        fields_found.append(another_result)

    return fields_found
2
ответ дан Becca Petrin 19 August 2018 в 06:57
поделиться
  • 1
    Вы можете использовать fields_found.extend (more_results) вместо запуска другого цикла. По-моему, выглядел бы немного чище. – sapit 15 June 2018 в 10:47

Я просто хотел повторить отличный ответ на @ hexerei-software с помощью yield from и принять списки верхнего уровня.

def gen_dict_extract(var, key):
    if isinstance(var, dict):
        for k, v in var.items():
            if k == key:
                yield v
            if isinstance(v, (dict, list)):
                yield from gen_dict_extract(v, key)
    elif isinstance(var, list):
        for d in var:
            yield from gen_dict_extract(d, key)
0
ответ дан Chris 19 August 2018 в 06:57
поделиться

Еще одна вариация, которая включает вложенный путь к найденным результатам ( note: эта версия не рассматривает списки ):

def find_all_items(obj, key, keys=None):
    """
    Example of use:
    d = {'a': 1, 'b': 2, 'c': {'a': 3, 'd': 4, 'e': {'a': 9, 'b': 3}, 'j': {'c': 4}}}
    for k, v in find_all_items(d, 'a'):
        print "* {} = {} *".format('->'.join(k), v)    
    """
    ret = []
    if not keys:
        keys = []
    if key in obj:
        out_keys = keys + [key]
        ret.append((out_keys, obj[key]))
    for k, v in obj.items():
        if isinstance(v, dict):
            found_items = find_all_items(v, key, keys=(keys+[k]))
            ret += found_items
    return ret
2
ответ дан Dolan Antenucci 19 August 2018 в 06:57
поделиться
d = { "id" : "abcde",
    "key1" : "blah",
    "key2" : "blah blah",
    "nestedlist" : [ 
    { "id" : "qwerty",
        "nestednestedlist" : [ 
        { "id" : "xyz", "keyA" : "blah blah blah" },
        { "id" : "fghi", "keyZ" : "blah blah blah" }],
        "anothernestednestedlist" : [ 
        { "id" : "asdf", "keyQ" : "blah blah" },
        { "id" : "yuiop", "keyW" : "blah" }] } ] } 


def fun(d):
    if 'id' in d:
        yield d['id']
    for k in d:
        if isinstance(d[k], list):
            for i in d[k]:
                for j in fun(i):
                    yield j

>>> list(fun(d))
['abcde', 'qwerty', 'xyz', 'fghi', 'asdf', 'yuiop']
36
ответ дан kev 19 August 2018 в 06:57
поделиться
  • 1
    Это рекурсивный генератор. Очень хорошее решение! – ovgolovin 21 March 2012 в 17:48
  • 2
    Единственное, что я бы изменил, это for k in d - for k,value in d.items() с последующим использованием value вместо d[k]. – ovgolovin 21 March 2012 в 17:49
  • 3
    Спасибо, это отлично работает. Требуется очень небольшая модификация, потому что мои списки могут содержать строки, а также dicts (которые я не упоминал), но в противном случае совершенны. – Matt Swain 21 March 2012 в 17:59
  • 4
    Это подходит для очень узкого случая, вы обязаны сделать это для себя, чтобы рассмотреть ответ от «hexerei software». gen_dict_extract – Bruno Bronosky 20 February 2017 в 05:24
  • 5
    Я получил ошибку & quot; TypeError: аргумент типа "NoneType" не является итерируемым & quot; – edge27 15 June 2018 в 08:11
Другие вопросы по тегам:

Похожие вопросы: