2016-10-18 218 views
4

我想遞歸地解析字典中的所有值爲ast.literal_eval(value)的字符串,但如果字符串不包含字典,則不會執行該eval。我想要這樣做,因爲我在字典中有一個字符串,它本身就是一個字典,我希望這個字典是一個字典。最好能舉個例子如何檢查一個字符串是否包含字典

my_dict = {'a': 42, 'b': "my_string", 'c': "{'d': 33, 'e': 'another string'}"} 

現在我不想做ast.literal_eval(my_dict['c'])我希望有一個通用的解決方案,我可以做convert_to_dict(my_dict)

我想寫我自己的方法,但我不知道如何檢查字符串是否包含字典,然後ast.literal_eval將失敗,因此問題。

+3

我認爲戰略是EVAL,然後運行'isinstance'或類似的檢查,如果它是一個字典 –

+0

東西你能做出一個例子嗎?因爲如果我遞歸地評估字典中的值,我會得到一個錯誤。 – Hakaishin

+3

在try/except中使用literal_eval,檢查它是否是嘗試中的字典,並將該鍵重新分配給新值 –

回答

0

其他的答案都非常好,使我對正確的解決方案,但先前接受的答案了一個錯誤。這是我工作的解決方案:

def recursive_dict_eval(myDict): 
    for key,value in myDict.items(): 
     try: 
      if(isinstance(value, dict)): 
       recursive_dict_eval(value) 
      evaled_value=ast.literal_eval(value) 
      assert isinstance(evaled_value,dict) 
      myDict[key]=recursive_dict_eval(evaled_value) 

     except (SyntaxError, ValueError, AssertionError): 
      #SyntaxError, ValueError are for the literal_eval exceptions 
      pass 
    return myDict 
0

如果您需要處理嵌套str定義dictjson.loads with an object_hook可能會爲你工作:

import json 

def convert_subdicts(d): 
    for k, v in d.items(): 
     try: 
      # Try to decode a dict 
      newv = json.loads(v, object_hook=convert_subdicts) 
     except Exception: 
      continue 
     else: 
      if isinstance(newv, dict): 
       d[k] = newv # Replace with decoded dict 
    return d 

origdict = {'a': 42, 'b': "my_string", 'c': "{'d': 33, 'e': 'another string'}"} 
newdict = convert_subdicts(origdict.copy()) # Omit .copy() if mutating origdict okay 

這應該遞歸處理,其中包含dict S可能會發生包含定義subdicts str價值觀的情況。如果您不需要處理該情況,則可以省略使用object_hook,或者用ast.literal_eval完全替換json.loads

1

我在上面的評論中引用的一般思想是通過字典運行並嘗試和評估。將其存儲在局部變量中,然後檢查評估的表達式是否是字典。如果是,則將其重新分配給通過的輸入。如果沒有,請保持獨立。

my_dict = {'a': 42, 'b': "my_string", 'c': "{'d': 33, 'e': 'another string'}"} 

def convert_to_dict(d): 
    for key, val in d.items(): 
     try: 
      check = ast.literal_eval(val) 
     except: 
      continue 
     if isinstance(check, dict): 
      d[key] = check 
    return d 

convert_to_dict(my_dict) 
+0

如果沒有異常拋出,你可以在except塊中繼續'並且/或者在'except'塊後面使用'else:'來執行'isinstance'和賦值。比需要設置check = False更好,並且當你知道它無效時再進行測試。 – ShadowRanger

1

您可以檢查您是否已經使用literal_eval並重新分配後的字典:

from ast import literal_eval 

def reassign(d): 
    for k, v in d.items(): 
     try: 
      evald = literal_eval(v) 
      if isinstance(evald, dict): 
       d[k] = evald 
     except ValueError: 
      pass 

只是通過在字典:

In [2]: my_dict = {'a': 42, 'b': "my_string", 'c': "{'d': 33, 'e': 'another stri 
    ...: ng'}"} 

In [3]: reassign(my_dict) 

In [4]: my_dict 
Out[4]: {'a': 42, 'b': 'my_string', 'c': {'d': 33, 'e': 'another string'}} 

In [5]: my_dict = {'a': '42', 'b': "my_string", '5': "{'d': 33, 'e': 'another st 
...: ring', 'other_dict':{'foo':'bar'}}"} 
In [6]: reassign(my_dict) 
In [7]: my_dict 
Out[7]: 
{'5': {'d': 33, 'e': 'another string', 'other_dict': {'foo': 'bar'}}, 
'a': '42', 
'b': 'my_string'} 

你也應該知道,如果您在字典中有某些其他對象,例如datetime對象等。然後,literal_eval將失敗,因此它是真實的取決於你的字典可以包含的內容是否會起作用。

如果您需要遞歸方法,您只需要在新字典上調用重新分配。

def reassign(d): 
    for k, v in d.items(): 
     try: 
      evald = literal_eval(v) 
      if isinstance(evald, dict): 
       d[k] = evald 
       reassign(evald) 
     except ValueError: 
      pass 

而且又剛剛通過字典:

In [10]: my_dict = {'a': 42, 'b': "my_string", 'c': "{'d': 33, 'e': \"{'f' : 64} 
    ...: \"}"} 

In [11]: reassign(my_dict) 

In [12]: my_dict 
Out[12]: {'a': 42, 'b': 'my_string', 'c': {'d': 33, 'e': {'f': 64}}} 

如果你想有一個新的字典:

from ast import literal_eval 
from copy import deepcopy 

def reassign(d): 
    for k, v in d.items(): 
     try: 
      evald = literal_eval(v) 
      if isinstance(evald, dict): 
       yield k, dict(reassign(evald)) 
     except ValueError: 
      yield k, deepcopy(v) 

,這將給你一個新的字典:

In [17]: my_dict = {'a': [1, 2, [3]], 'b': "my_string", 'c': "{'d': 33, 'e': \"{ 
    ...: 'f' : 64}\"}"} 

In [18]: new = dict(reassign(my_dict)) 

In [19]: my_dict["a"][-1].append(4) 

In [20]: new 
Out[20]: {'a': [1, 2, [3]], 'b': 'my_string', 'c': {'d': 33, 'e': {'f': 64}}} 

In [21]: my_dict 
Out[21]: 
{'a': [1, 2, [3, 4]], 
'b': 'my_string', 
'c': '{\'d\': 33, \'e\': "{\'f\' : 64}"}'} 

您需要確保深層複製對象,或者當您有像上面列表的列表一樣的嵌套對象時,您將不會得到字典的真實獨立副本。

2

這是一個處理遞歸的命題。正如評論中所建議的那樣,它試圖評估一切,然後檢查結果是否是字典,如果是我們遞歸的,否則我們跳過該值。我sligthly改變了最初的字典,以顯示它hanldes recusion罰款:

import ast 
my_dict = {'a': 42, 'b': "my_string", 'c': "{'d': 33, 'e': \"{'f' : 64}\"}"} 

def recursive_dict_eval(old_dict): 
    new_dict = old_dict.copy() 
    for key,value in old_dict.items(): 
     try: 
      evaled_value=ast.literal_eval(value) 
      assert isinstance(evaled_value,dict) 
      new_dict[key]=recursive_dict_eval(evaled_value) 

     except (SyntaxError, ValueError, AssertionError): 
      #SyntaxError, ValueError are for the literal_eval exceptions 
      pass 
    return new_dict 

print(my_dict) 
print(recursive_dict_eval(my_dict)) 

輸出:

{'a': 42, 'b': 'my_string', 'c': '{\'d\': 33, \'e\': "{\'f\' : 64}"}'} 
{'a': 42, 'b': 'my_string', 'c': {'e': {'f': 64}, 'd': 33}} 
相關問題