【问题标题】:How to check if a string contains a dictionary如何检查字符串是否包含字典
【发布时间】:2017-02-27 20:36:35
【问题描述】:

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

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会失败,所以问题来了。

【问题讨论】:

  • 我认为策略是 eval 然后运行 ​​isinstance 或类似的东西来检查它是否是一个字典
  • 你能举个例子吗?因为如果我递归地评估 dict 中的值,我会得到一个错误。
  • 在try/except中使用literal_eval,在try中检查它是否是一个dict,并将key重新分配给新值
  • @PadraicCunningham,为什么不回答这个问题?

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


【解决方案1】:

这是一个处理递归的命题。正如 cmets 中所建议的那样,它尝试评估所有内容,然后检查结果是否为 dict,如果是我们递归,否则我们跳过 value。我稍微改变了最初的 dict 以表明它可以很好地处理 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}}

【讨论】:

    【解决方案2】:

    您可以在使用 literal_eval 并重新分配后检查您是否有 dict:

    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 可以包含的内容是否会工作与否。

    如果您需要递归方法,您只需在新字典上调用 reassign。

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

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

    【讨论】:

      【解决方案3】:

      我上述评论中引用的一般想法是通过字典运行并尝试评估。将其存储在局部变量中,然后检查评估的表达式是否是字典。如果是这样,则将其重新分配给传递的输入。如果没有,请不要理会它。

      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)
      

      【讨论】:

      • 您可以在except 块中使用continue 和/或在except 块之后使用else: 来仅执行isinstance 并在没有引发异常的情况下进行赋值。比需要设置 check = False 并稍后在您知道它无效时对其进行测试要好。
      【解决方案4】:

      如果您需要处理嵌套的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
      

      这应该递归处理包含的dicts 可能包含定义子字典的strs 值的情况。如果您不需要处理这种情况,您可以省略使用object_hook,或者将json.loads 完全替换为ast.literal_eval

      【讨论】:

        【解决方案5】:

        其他答案非常好,并引导我找到正确的解决方案,但之前接受的答案有一个错误。这是我的工作解决方案:

        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
        

        【讨论】:

          猜你喜欢
          • 2013-03-04
          • 2015-11-12
          • 1970-01-01
          • 1970-01-01
          • 2017-09-23
          • 1970-01-01
          • 2020-12-12
          • 1970-01-01
          • 2016-11-06
          相关资源
          最近更新 更多