У меня есть вложенный dict, который может содержать заполнители как для ключей, так и для значений.

example_dict = {'dict1': {'%(map3)s': {'data': 'tmp'},
                                 '%(map2)s': {'freshdata': 'testtest'}},
             'dict2': {'%(map3)s': {'data': '%(map1)s'}, '%(map3)s': {'status': 'available'}}}

У меня есть сопоставление с сопоставлением заполнителя:

mapping_dict= {
    "map1": [1,2,2],
    "map2": "qwerz",
    "map3": "asdfasdf"
}

Если заполнитель находится в позиции VALUE, также может случиться так, что соответствующее отображение mapping_dict содержит другой тип данных, чем строка в качестве значения, например список или int. Как я могу передать этот тип данных в исходный dict? Я не знаю, как сделать заполнитель, например для списка.

Информация : может случиться так, что mapping_dict содержит больше ключей, чем содержит данный example_dict.

Я хотел бы иметь функцию, которая заменяет заполнители данного dict значениями mapping_dict.

Что было бы для этого хорошей рекурсивной реализацией?

1
eljobso 5 Сен 2016 в 20:11

3 ответа

Лучший ответ

Используя стек для навигации по парам «ключ-значение», вы можете выдвигать ключи для их переименования и для значений делать то же самое, за исключением попытки и посмотреть, можете ли вы оценить их как литералы Python, используя ast.literal_eval для обработки вашего списка.

import ast
from copy import deepcopy

example_dict = {
    'dict1': {
        '%(map3)s': {'data': 'tmp'},
        '%(map2)s': {'freshdata': 'testtest'}
    },
    'dict2': {
        '%(map3)s': {'data': '%(map1)s'}
    }
}

mapping_dict= {
    "map1": [1,2,2],
    "map2": "qwerz",
    "map3": "asdfasdf"
}

def sub_placeholders(orig, subs):
    d = deepcopy(orig)
    todo = [d]
    while todo:
        nxt = todo.pop()
        for k, v in nxt.items():
            nxt[k % mapping_dict] = nxt.pop(k)
            if isinstance(v, dict):
                todo.append(v)
            elif isinstance(v, str):
                nxt[k] = v % subs
                try:
                    nxt[k] = ast.literal_eval(nxt[k])
                except ValueError:
                    pass
    return d

Запуск sub_placeholders(example_dict, example_mapping) даст вам:

{'dict1': {'asdfasdf': {'data': 'tmp'}, 'qwerz': {'freshdata': 'testtest'}},
 'dict2': {'asdfasdf': {'data': [1, 2, 2]}}}
3
Jon Clements 5 Сен 2016 в 19:40

Вот один рекурсивный вариант, удаляющий существующие ключи и добавляющий к ним новые ключи, применяя «формат» с «именованными» заполнителями. Примечание: в этом случае мы модифицируем входной словарь:

from pprint import pprint

example_dict = {'dict1': {'%(map1)s': {'data': 'tmp'}, '%(map2)s': {'freshdata': 'testtest'}},
                'dict2': {'%(map1)s': {'data': '%(map1)s'}, '%(map2)s': {'status': 'available'}}}

mapping_dict= {
    "map1": "asdf",
    "map2": "qwerz",
}


def apply_placeholder(d, placeholder):
    for key, value in d.items():
        del d[key]
        if isinstance(value, dict):
            d[key % placeholder] = value
            apply_placeholder(value, placeholder)
        else:
            d[key % placeholder] = value % placeholder


apply_placeholder(example_dict, mapping_dict)
pprint(example_dict)

Печать:

{'dict1': {'asdf': {'data': 'tmp'}, 'qwerz': {'freshdata': 'testtest'}},
 'dict2': {'asdf': {'data': 'asdf'}, 'qwerz': {'status': 'available'}}}

Мне не очень нравятся здесь вызовы del и изменение входного объекта, и я был бы рад увидеть лучший вариант.

3
alecxe 5 Сен 2016 в 17:23

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

from pprint import pprint
import copy

try:
    stringtype = basestring
except NameError:
    stringtype = str  # Python 3

def subst(mapping, replacements):

    def do_subst(mapping, replacements):
        for k, v in list(mapping.items()):
            newk, newv = k, v
            changed = False

            if isinstance(k, stringtype):
                newk = k % replacements
                if newk != k:
                    changed = True

            if isinstance(v, stringtype):
                newv = v % replacements
                if newv != v:
                    changed = True
            elif isinstance(v, dict):
                newv = do_subst(v, replacements)
                if newv != v:
                    changed = True

            if changed:
                del mapping[k]
                mapping[newk] = newv

        return mapping

    return do_subst(copy.deepcopy(mapping), replacements)

example_dict = {'dict1': {'%(map1)s': {'data': 'tmp'},
                          '%(map2)s': {'freshdata': 'testtest'}},
                'dict2': {'%(map1)s': {'data': '%(map1)s'},
                          '%(map2)s': {'status': 'available'}}}

mapping_dict= {"map1": "asdf", "map2": "qwerz"}

print('Before')
pprint(example_dict)
result = subst(example_dict, mapping_dict)
print('After')
pprint(result)

Выход:

 Before
 {'dict1': {'%(map1)s': {'data': 'tmp'}, '%(map2)s': {'freshdata': 'testtest'}},
  'dict2': {'%(map1)s': {'data': '%(map1)s'},
            '%(map2)s': {'status': 'available'}}}
 After
 {'dict1': {'asdf': {'data': 'tmp'}, 'qwerz': {'freshdata': 'testtest'}},
  'dict2': {'asdf': {'data': 'asdf'}, 'qwerz': {'status': 'available'}}}
1
martineau 5 Сен 2016 в 19:10