2015-11-09 3 views
3

Например, у меня есть следующий Dict:Более вещий способ изменить многоуровневый словарь значений

{'foo': 'test', 
'bar': {'test1': 'some text here'}, 
'baz': {'test2': {'test3': 'some text here', 'test4': 'some text here'}}} 

Или что-то вроде этого, может быть, больше уровней.

Теперь вопрос в том, что я хочу изменить ... 'some text here' до 'A test'. Да, я могу использовать много for петель как следующий код:

d = {'foo': 'test', 
    'bar': {'test1': 'some text here'}, 
    'baz': {'test2': {'test3': 'some text here', 
         'test4': 'some text here'}}} 

for i in d: 
    if d[i] == 'some text here': 
     d[i] = 'A test' 

    elif type(d[i]) == dict: 
     for j in d[i]: 
      if d[i][j] == 'some text here': 
       d[i][j] = 'A test' 

      elif type(d[i][j]) == dict: 
       for n in d[i][j]: 
        if d[i][j][n] == 'some text here': 
         d[i][j][n] = 'A test' 

__import__('pprint').pprint(d) 

Выход:

{'bar': {'test1': 'A test'},              
'baz': {'test2': {'test3': 'A test', 'test4': 'A test'}}, 
'foo': 'test'} 

Однако я не думаю, что это хороший способ ... Есть идеи?

+0

@ user2393267: Это называется тегами, а не метками :) –

+0

Каков критерий замены, точное соответствие любого значения на любой глубине? –

+0

@ qarma: Ну, на самом деле я сказал, что в моем вопросе: * Может быть, больше уровней *. –

ответ

0

Пока ценности вы меняете не слишком волосатый, вот один вкладыш:

In [29]: data = dict(foo=12, bar=dict(dd=12), ex=dict(a=dict(b=dict(v=12)))) 

In [30]: json.loads(re.sub("12", "33", json.dumps(data))) 
Out[30]: {'bar': {'dd': 33}, 'ex': {'a': {'b': {'v': 33}}}, 'foo': 33} 

EDIT, на Кевина, простая замена не нужно даже re:

json.loads(json.dumps(data).replace("12", "33")) 
+1

Ну ... Почему я не пробовал это ... Отлично! BTW, два раза: 1. это также заменит ключи, если в ключах есть '12', возможно, используйте': 33'' вместо '' 33''? 2. Почему бы просто не использовать 'str.replace()'? –

+1

Хорошая точка, '.replace()' еще лучше! Пока значения уникальны. Я использовал 're' специально для возможных особых случаев, таких как ключи. –

+0

Если вы собираетесь переборщить это так, зачем даже беспокоиться с json, когда str и eval могут делать то, что вы хотите 'eval (str ({'foo': 33}). Replace ('33 ',' 11 ')) '. – justinfay

3

Это похоже на хороший случай для рекурсии.

import re 

def replace_rec(data, search, replace, _pattern=None): 
    if _pattern is None: 
     _pattern = re.compile(r'^%s$' % search) 
    for k, v in data.items(): 
     try: 
      data[k] = _pattern.sub(replace, v) 
     except TypeError: 
      try: 
       replace_rec(data[k], search, replace, _pattern=_pattern) 
      except AttributeError: 
       # Leave any other types as they are. 
       continue 

Используйте его для примера, как это:

>>> data = { 
...  'foo': 'test', 
...  'bar': {'test1': 'some text here'}, 
...  'baz': {'test2': {'test3': 'some text here', 'test4': 'some text here'}}, 
...  'loc': [1, 2, 3], 
...  'fp': 'foo some text here foo', 
... } 
>>> replace_rec(data, 'some text here', 'A test') 
>>> pprint.pprint(data) 
{'bar': {'test1': 'A test'}, 
'baz': {'test2': {'test3': 'A test', 'test4': 'A test'}}, 
'foo': 'test', 
'fp': 'foo some text here foo', 
'loc': [1, 2, 3]} 
1

немного альтернативный вариант. Это правильно обрабатывает не-строки в словарях и заменяет точное совпадение текста.

def replace(d, find_text, replace_text): 
    for k, v in d.items(): 
     if isinstance(v, dict): 
      replace(v, find_text, replace_text) 
     elif isinstance(v, str): 
      if v == find_text: 
       d[k] = replace_text 

d = { 
    'test': 'dont change some text here', 
    'ignore' : 42, 

    'foo': 'test', 
    'bar': {'test1': 'some text here'}, 
    'baz': {'test2': {'test3': 'some text here', 'test4': 'some text here'}}}  

replace(d, 'some text here', 'A test') 

__import__('pprint').pprint(d) 

Это выведет:

{'bar': {'test1': 'A test'}, 
'baz': {'test2': {'test3': 'A test', 'test4': 'A test'}}, 
'foo': 'test', 
'ignore': 42, 
'test': 'dont change some text here'} 
+0

Ну, я думаю, что нет большой разницы между вашим ответом и ранее принятым ответом justinfay. Поэтому в его ответе есть проблема с не-строками, может быть, комментарий будет лучше, чем другой ответ. –

+0

Его ответ также заменит «не меняйте текст здесь». Из вашего вопроса неясно, должен ли этот случай также быть заменен. –

+0

Хм ... понимаю, поэтому его ответ может решить мой вопрос, этого достаточно. И ваш вопрос более полезен, если мой дикт был более сложным. правильно? Я подтвердил ваш ответ. –

1

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

Сначала получите ссылки на все словари (на всех уровнях), затем измените их.

def all_dicts(d): 
    yield d 
    for v in d.values(): 
     if isinstance(v, dict): 
      yield from all_dicts(v) 

data = dict(foo=12, bar=dict(dd=12), ex=dict(a=dict(b=dict(v=12)))) 

for d in all_dicts(data): 
    for k, v in d.items(): 
     if v == 12: 
      d[k] = 33 
Смежные вопросы