@rJIynbIuKOT

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

Есть крайне глубокий yaml-файл, до 20 уровней. Маленький кусочек для примера:
key-vars-test.yaml

default:
  ui-key:
    ferg: Привет
    gtrh: Пока
    wiki:
      blocks_common: Пример
      blocks_common_desktop:
        blocks_b-access-button:
          external_access: не только
          lock: Закрытый
          owner: владелец
          restricted: Ограниченный
        blocks_b-action: Пример 2
        blocks_b-action-copy:
          cant_copy_cluster: Не удалось скопировать раздел
    data:
      aside-header:
        popover:
          button_watch: Смотреть
          description: Основы работы с интерфейсом, рекомендации и не только!
          description-ext: Создайте свой первый алгоритм за 2 минуты
          close-confirm: >-
            У вас есть непримененные изменения. Вы уверены, что хотите закрыть
            диалог?
      chartkit:
        button-spin-input:
          error: Ошибка
        categories: Категории
        categories_test:
          label_ya-podcasts-conn-description: >-
            Это подключение позволит вам просматривать детальную статистику
            прослушивания ваших подкастов.


            По кнопке ниже вы получите ключ (OAuth-токен) — инструмент, который
            позволит системе DataLens отобразить информацию по вашим подкастам.


            Вы сможете самостоятельно выдавать доступы к статистике другим
            людям, как описано в [документации]({{docLink}}). Доступ можно в
            любой момент закрыть, отозвав выписанный токен.
          label_ya-podcasts-conn: 123ddd



Мне надо получить конечные значения со всеми предыдущими ключами в определенном виде. Пример ожидаемого результата для приведенного выше кусочка:
key-vars.yaml

default: {{ ui-key.ferg }} : Привет
default: {{ ui-key.gtrh }} : Пока
default: {{ ui-key.wiki.blocks_common }} : Пример
default: {{ ui-key.wiki.blocks_common_desktop.blocks_b-access-button.external_access }} : не только
default: {{ ui-key.data.aside-header.popover.button_watch }} : Смотреть
default: {{ ui-key.data.aside-header.popover.description }} : Основы работы с интерфейсом, рекомендации и не только!
default: {{ ui-key.data.chartkit.button-spin-input.error }} : Ошибка
default: {{ ui-key.data.chartkit.categories }} : Категории
default: {{ ui-key.data.chartkit.categories_test.label_ya-podcasts-conn-description }} : 134
default: {{ ui-key.data.chartkit.categories_test.label_ya-podcasts-conn }} : 123ddd



Я использовал библиотеку pyyaml, сделал из yaml-файла словарь, а потом прошелся по каждому элементу и добавлял каждый элементы в список, пока он не перестал быть словарем. В конце преобразовал список в файл. Но так как уровней около 20, получилась безумная лестница. Код, который у меня получился и работает:
for-habr.py

import yaml

contents = []

with open("key-vars-test.yaml", "r", encoding='utf-8') as file:
    test = yaml.load(file, Loader=yaml.FullLoader)
    
#print(test){{{{

for key1, value1 in test.items():
    for key2, value2 in value1.items():
        for key3, value3 in value2.items():
            if type (value3) is dict:
                for key4, value4 in value3.items():
                    if type (value4) is dict:
                        for key5, value5 in value4.items():
                            if type (value5) is dict:
                                for key6, value6 in value5.items():
                                    if type (value6) is dict:
                                        for key7, value7 in value6.items():
                                            if type (value7) is dict:
                                                for key8, value8 in value7.items():
                                                    contents.append(f"{key1}: {{{{ {key2}.{key3}.{key4}.{key5}.{key6}.{key7}.{key8} }}}} : {value8}")
                                            else:
                                                contents.append(f"{key1}: {{{{ {key2}.{key3}.{key4}.{key5}.{key6}.{key7} }}}} : {value7}")
                                                #print(f"{key1}: {{{{ {key2}.{key3}.{key4}.{key5}.{key6}.{key7} }}}} : {value7}")
                                    else:
                                        contents.append(f"{key1}: {{{{ {key2}.{key3}.{key4}.{key5}.{key6} }}}} : {value6}")
                                        #print(f"{key1}: {{{{ {key2}.{key3}.{key4}.{key5}.{key6} }}}} : {value6}")
                            else:
                                contents.append(f"{key1}: {{{{ {key2}.{key3}.{key4}.{key5} }}}} : {value5}")
                                #print(f"{key1}: {{{{ {key2}.{key3}.{key4}.{key5} }}}} : {value5}")
                    else:
                        contents.append(f"{key1}: {{{{ {key2}.{key3}.{key4} }}}} : {value4}")
            else:
                contents.append(f"{key1}: {{{{ {key2}.{key3} }}}} : {value3}")

with open("key-vars.yaml", "w", encoding='utf-8') as file:
    file.writelines("%s\n" % i for i in contents)



Прошу помочь оптимизировать код, избавившись от такой безумной вложенности. Наверняка можно ограничиться пару циклами, обновляя словарь, над которым проводятся манипуляции, но я никак не могу придумать как.
UPD: Так как запрос готового кода это нарушение п.5.12 Регламента, то общий ответ "обход вложенного словаря" получен, спасибо.
UPD2: По итогу больше помог ответ "распаковка словаря", чем "обход вложенного словаря".
Нашел библиотеку flatten-dict, которая полностью решает проблему преобразования вложенных словарей в один плоский.

Итоговый рабочий код, который настроен под мой определенный вид:
for-habr-finally.py

import yaml
from flatten_dict import flatten
from flatten_dict.reducers import make_reducer

key_vars = []

with open("key-vars-test.yaml", "r", encoding='utf-8') as file:
    key_vars_dict = yaml.load(file, Loader=yaml.FullLoader)

key_vars_flat_dict = flatten(key_vars_dict, reducer=make_reducer(delimiter='.'))

for key, value in key_vars_flat_dict.items():
    key_vars.append(f"{{{{ {key[8:]} }}}} : {value}")

with open("key-vars.yaml", "w", encoding='utf-8') as file:
    file.writelines("%s\n" % i for i in key_vars)

  • Вопрос задан
  • 171 просмотр
Решения вопроса 1
@rJIynbIuKOT Автор вопроса
По итогу больше помог ответ "распаковка словаря", чем "обход вложенного словаря".
Нашел библиотеку flatten-dict, которая полностью решает проблему преобразования вложенных словарей в один плоский.

Итоговый рабочий код, который настроен под мой определенный вид:
for-habr-finally.py

import yaml
from flatten_dict import flatten
from flatten_dict.reducers import make_reducer

key_vars = []

with open("key-vars-test.yaml", "r", encoding='utf-8') as file:
    key_vars_dict = yaml.load(file, Loader=yaml.FullLoader)

key_vars_flat_dict = flatten(key_vars_dict, reducer=make_reducer(delimiter='.'))

for key, value in key_vars_flat_dict.items():
    key_vars.append(f"{{{{ {key[8:]} }}}} : {value}")

with open("key-vars.yaml", "w", encoding='utf-8') as file:
    file.writelines("%s\n" % i for i in key_vars)

Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы