LINUX.ORG.RU

python - dict.get() для большого словаря сложной структуры

 


0

3

Привет!

У меня есть словарь со сложной структурой с кучей значений, из которого мне нужно полуить определенное значение «из глубины» или вернуть дефолтное значение. Если бы словарь был простым, я воспользовался бы .get(), но лепить его для каждого вложенного элемента мне не хочется. Плюс внутри словарей попадаются списки, есть функции для которых мне нужно получить значение (и они возвращают тоже подобные сложные структуры), ну и так далее. То есть выглядит это примерно так (упрощенно):

print(foo['bar']['baz'][1]['qwerty'][2].get_data())

Хочу задействовать функцию, внутри которой был бы простой try\except-блок, ну вроде

def f1(data, default=''):
  try:
    return data
  except:
    return default

Но никак не могу нагуглить как можно передать значение из примера выше не вычисляя его при подстановке. Ну например нет у меня ключа 'baz' в словаре, которой лежит в ключе 'bar' словаря 'foo', и я ожидаемо получаю ошибку при вызове своего f1. Пытался передавать как функцию, пытался использовать LazyProxy из objproxies, но успеха не добился.

Как можно решить мою проблему не обвешивая код try\except'ами, 'key' in dict и len(list) > N?

Заранее благодарен за советы!

★★★

Последнее исправление: micronekodesu (всего исправлений: 1)

Ты умудрился сделать так, что даже ЯП тебя унижает, молодец.

Но никак не могу нагуглить как можно п...

не распарсил, но почему у тебя не работает что-то вроде (либо рекурсивная реализация):

def f1(source, *args, default=''):
  d = source
  for a in args:
    try:
      d = d[a]
    except:
      return default
  return d

d = f1(source, 'bar', 'baz', 1, 'qwerty', 1, default=None)

А правильный путь - переписать эту твою raw-структуру на разные типы контейнеров, c хранимыми в них entity, реализующие удобный тебе интерфейс с сеттерами/геттерами/поиском.

irr123
()
Ответ на: комментарий от irr123

d = f1(source, 'bar', 'baz', 1, 'qwerty', 1, default=None)

Это, конечно, рабочий вариант, но очевидно что он «многословен», мне хочется что-то более простое. К тому же у меня есть функции внутри словарей (ну точнее методы объектов), что только «утяжелит» конструкцию.

А правильный путь - переписать эту твою raw-структуру

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

Ты умудрился сделать так, что даже ЯП тебя унижает, молодец.

Один ты Д'Артаньян.

micronekodesu ★★★
() автор топика
Последнее исправление: micronekodesu (всего исправлений: 1)
Ответ на: комментарий от proud_anon

Позволяет выхватить key error на 'baz' (foo = {}), придется обходить все элементы и превращать их в defaultdict, не хотелось бы этого да и раз все придется перебирать проще сразу и проверить.

micronekodesu ★★★
() автор топика
Ответ на: комментарий от anonymous

Опять же мне нужно будет передавать отдельно «корневой» объект, и отдельно описывать путь до нужного элемента, не самый удобный вариант как по мне.

micronekodesu ★★★
() автор топика
Ответ на: комментарий от micronekodesu

А, я просто не так тебя понял. Тогда тебе нужно что-то вроде https://www.w3schools.com/xml/xpath_syntax.asp

https://stackoverflow.com/questions/7320319/xpath-like-query-for-nested-pytho...

def xpath_get(mydict, path):
    elem = mydict
    try:
        for x in path.strip("/").split("/"):
            elem = elem.get(x)
    except:
        pass

    return elem


foo = {
  'spam':'eggs',
  'morefoo': [{
               'bar':'soap',
               'morebar': {
                           'bacon' : {
                                       'bla':'balbla'
                                     }
                           }
              },
              'bla'
              ]
   }

print xpath_get(foo, "/morefoo/0/morebar/bacon")
anonymous
()

Поставь

pydash
(это порт из JS lodash на python) и делай себе
pydash.get(mydict,'key.key.key','default value')

abc
()

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

А для упрощения хождения по вложенной структуре можешь написать простейшую обёртку вида

def getpath(tree, key, *more_keys, default=None):
    if not more_keys:
        return tree.get(key, default)
    elif key in tree:
        return getpath(tree[key], *more_keys, default)
    else:
        return default

print(getpath(foo, 'bar', 'baz', 1, 'qwerty', 2))
slovazap ★★★★★
()
Ответ на: комментарий от slovazap

Я не понял что ты хочешь, зачем тебе ловить исключения в том что их не бросает и почему тебя не устраивает get, у которого, если что, есть аргумент для возвращения если значения не найдено

Ему не хочется писать obj.get("key0", "default").get("key1", "default").get("key2", "default").get("key3", "default") Кроме того, под ключами могут скрываться не только словари, но и списки, поэтому у объекта get'а может и не быть.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

Ему не хочется писать obj.get(«key0», «default»).get(«key1», «default»).get(«key2», «default»).get(«key3», «default»)

Я написал пример как этого не писать

Кроме того, под ключами могут скрываться не только словари

Я написал пример, весь код за него я писать не собираюсь. Любые типы, в том числе и собственные, в том числе и всякие .get_data() обрабатываются тривиально.

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

Я видел что ты написал, а если бы почитал тред то видел бы что это уже предлагали.

Любые типы, в том числе и собственные, в том числе и всякие .get_data() обрабатываются тривиально.

>>> class MyMegaClass:
...   def get_data(cls):
...     return 'ololo'
... 
>>> foo = {'bar': MyMegaClass()}
>>> getpath(foo, 'bar', 'get_data()', default='')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "./script.py", line 7, in getpath
    return getpath(tree[key], *more_keys, default)
  File "./script.py", line 6, in getpath
    elif key in tree:
TypeError: argument of type 'MyMegaClass' is not iterable
micronekodesu ★★★
() автор топика
Последнее исправление: micronekodesu (всего исправлений: 1)
Ответ на: комментарий от micronekodesu

Код-то для обработки MyMegaClass в getpath() Пушкин будет писать?

slovazap ★★★★★
()
Ответ на: комментарий от Virtuos86

get() сильно дофига медленный - и если на разовых запросах это фигня, то на длинных цепочках получится нехилый оверхед... ну эт так, вдруг кому интересно

genryRar ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.