如何检查字符串是否包含字典

时间:2016-10-18 14:06:52

标签: python string python-3.x dictionary recursion

我想以递归方式解析dict中的所有值,这些值是ast.literal_eval(value)的字符串,但如果字符串不包含字典,则不执行该eval。我想要这个,因为我在一个dict中有一个字符串,它本身就是一个字典,我希望它的值是一个字典。最好举个例子

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

现在我不想做ast.literal_eval(my_dict['c'])我想要一个通用的解决方案,我可以做convert_to_dict(my_dict)

我想编写自己的方法,但我不知道如何检查字符串是否包含dict,然后ast.literal_eval将失败,因此问题。

5 个答案:

答案 0 :(得分:2)

这是一个处理递归的命题。正如评论中所建议的那样,它会尝试评估所有内容然后检查结果是否为dict,如果是我们递归,否则我们跳过该值。我轻轻地改变了最初的词典,以表明它可以很好地解决错误:

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}}

答案 1 :(得分: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)

答案 2 :(得分: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

直接传入dict:

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'}

你还应该知道,如果你在dict中有某些其他对象,如 datetime 对象等,那么literal_eval会失败,所以它真的取决于你的dict可以包含的内容是否会工作与否。

如果您需要递归方法,您只需要调用新字典的重新分配。

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

再次通过dict:

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}"}'}

您需要确保 deepcopy 对象,否则当您拥有嵌套对象(如上面的列表列表)时,您将无法获得该dict的真正独立副本。

答案 3 :(得分: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可能包含定义subdicts的str值的情况。如果您不需要处理该案例,则可以省略object_hook的使用,或将json.loads完全替换为ast.literal_eval

答案 4 :(得分: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