【问题标题】:Crafting a python dictionary based on a .properties file基于 .properties 文件制作 python 字典
【发布时间】:2021-07-08 08:22:26
【问题描述】:

我想将 .properties 文件的键和值解析为 python 字典。 我正在解析的 .properties 文件使用以下语法(键和值是示例):

key1.subkey1.subsubkey1=value1
key1.subkey1.subsubkey2=value2
key1.subkey2=value3
key2=value4

所以每个值对应一个键,该键由一个或多个用句点分隔的级别组成。 目标是创建一个 Python 字典,其中每个键都是一个包含其值和子键的字典。字典应该是递归可迭代的,所以每个级别都应该遵循相同的结构。

前面的例子应该产生以下类型的字典:

'subKeys': 
  'key1':
    'subKeys':
      'subkey1': 
        'subKeys':
          'subsubkey1': 
            'val': 'value1'
          'subsubkey2': 
            'val': 'value2'
      'subkey2': 
        'val': 'value3'
  'key2':
    'val': 'value4'

我在 python 中使用以下算法循环它:

def setKeyAndValue(storageDict, rowParts):
    keyParts = rowParts[0].split('.')
    if not keyParts[0] in outputDict:
        storageDict[keyParts[0]] = {}
    newObj = storageDict[keyParts[0]]
    for i in range(len(keyParts)):
        if i == len(keyParts)-1:
            # Reached the end of the key, save value to dictionary
            newObj["val"] = rowParts[1]
        else :
            # Not yet at the end of the key
            if "subKeys" not in newObj:
                newObj["subKeys"] = {}
            if keyParts[i+1] not in newObj["subKeys"]:
                newObj["subKeys"][keyParts[i+1]] = {}
            newObj = newObj["subKeys"][keyParts[i+1]]

f = open("FILEPATH.properties", "r")
outputDict = {}
outputDict["subKeys"] = {}
outputDictSubKeys = outputDict["subKeys"]
for row in f:
    if not row.startswith('#') and not row.startswith('//'):
        parts = row.split('=', 1)
        if  len(parts)== 2:
            setKeyAndValue(outputDictSubKeys, parts)  
f.close()

结果字典 (outputDict) 缺少两个键值对 (key1.subkey1.subsubkey1=value1, key1.subkey1.subsubkey2=value2):

'subKeys': 
  'key1':
    'subKeys':
      'subkey2': 
        'val': 'value3'
  'key2':
    'val': 'value4'

我很确定问题出在以下行:

newObj = newObj["subKeys"][keyParts[i+1]]

我用循环的每次迭代替换字典中的 newObj。

有没有办法调整这个现有的算法以使其工作,如果没有,我应该如何重新开始?效率不是问题,属性文件不是很大。

【问题讨论】:

  • 你的代码中的 test1 是什么?
  • 对不起,这是 outputDict 的原始名称,为了清楚起见,我将其重命名。

标签: python python-3.x dictionary parsing properties-file


【解决方案1】:

一些观察:

  • 您尝试做的事情与Trie 密切相关。您正在生成具有最终值的通用前缀。
  • 对于嵌套结构,来自functools 的内置reduce 函数将变得非常有用。

解决方案

数据

现在假设我们有一个名为data.properties 的文件,其结构如下

key1.subkey1.subsubkey1=value1
key1.subkey1.subsubkey2=value2
key1.subkey2=value3
key2=value4

代码

那么我们可以使用下面的代码

import functools
from collections import defaultdict
from pprint import pprint

if __name__ == '__main__':
    node = lambda: defaultdict(node)
    trie = node()

    with open("data.properties", 'r') as file:
        for line in file.readlines():
            key, value = line.strip().split('=')
            functools.reduce(dict.__getitem__, key.split('.'), trie)

            val = (functools.reduce(lambda d, key: d.get(key), key.split('.')[:-1], trie))
            val[key.split('.')[-1]] = value

    pprint(trie)

输出

生成以下输出

defaultdict(<function <lambda> at 0x000002054579EF70>,
            {'key1': defaultdict(<function <lambda> at 0x000002054579EF70>,
                                 {'subkey1': defaultdict(<function <lambda> at 0x000002054579EF70>,
                                                         {'subsubkey1': 'value1',
                                                          'subsubkey2': 'value2'}),
                                  'subkey2': 'value3'}),
             'key2': 'value4'})

defaultdict 可以忽略,只要键在字典中即可。如果要删除defaultdict属性,可以使用这个函数

def defaultdict_to_dict(data):
    """ Convert a nested defaultdict to a normal dictionary.  """
    if isinstance(data, defaultdict):
        data = dict(data)
    if isinstance(data, dict):
        for k, v in data.items():
            data[k] = defaultdict_to_dict(v)
    return data

并使用pprint(defaultdict_to_dict(trie)) 调用它。 这将输出

{'key1': {'subkey1': {'subsubkey1': 'value1', 
                      'subsubkey2': 'value2'},
          'subkey2': 'value3'},
 'key2': 'value4'}

说明

大部分魔法发生在以下两行

1) functools.reduce(dict.__getitem__, key.split('.'), trie)

2) val = (functools.reduce(lambda d, key: d.get(key), key.split('.')[:-1], trie))

在第一行中,我们为所有键创建了 Trie,所有值(叶子)都是defaultdict。这意味着它可以任意深度扩展(拥有任意数量的子键)。

在第二行中,我们正在遍历 Trie,按照键,直到最后一个键。以key1.subkey1.subsubkey1=value1为例,这段代码会类似于

val = trie['key1']['subkey1']

下一行 (val[key.split('.')[-1]] = value) 等于

trie['key1']['subkey1']['subsubkey1'] = 'value1'

【讨论】:

  • 虽然这个答案对未来的 python 开发很有帮助,但我选择了 Sunny Jain 的答案,因为它需要对我现有的代码进行最少的更改。谢谢你的解释!
【解决方案2】:

我复制了您的函数并测试了您的代码并进行了一些更改。下面的代码工作正常。

def setKeyAndValue(storageDict, rowParts):
    print rowParts
    keyParts = rowParts[0].split('.')
    if not keyParts[0] in storageDict.keys():
            storageDict[keyParts[0]] = {}
    newObj = storageDict[keyParts[0]]
    for i in range(len(keyParts)):
            if i == len(keyParts)-1:
                    # Reached the end of the key, save value to dictionary
                    newObj["val"] = rowParts[1]
            else :
                    # Not yet at the end of the key
                    if "subKeys" not in newObj:
                            newObj["subKeys"] = {}
                    if keyParts[i+1] not in newObj["subKeys"]:
                            newObj["subKeys"][keyParts[i+1]] = {}
                    newObj = newObj["subKeys"][keyParts[i+1]]



def main():
    input  = [
            'key1.subkey1.subsubkey1=value1',
            'key1.subkey1.subsubkey2=value2',
            'key1.subkey2=value3',
            'key2=value4'
    ]
    ans = {}
    ans1 = {
            'subKeys': ans
    }

    for row in input:
            parts = row.split('=', 1)
            setKeyAndValue(ans, parts)
    print ans1

main()

输出如下:

{'subKeys': {'key2': {'val': 'value4'}, 'key1': {'subKeys': {'subkey2': {'val': 'value3'}, 'subkey1': {'subKeys': {'subsubkey1': {'val': 'value1'}, 'subsubkey2': {'val': 'value2'}}}}}}}

storageDict.keys() 替换您的OutputDict 变量并编写了一个示例主函数。尝试自己运行它,看看它是否适合您。

我认为您的 OutputDict 仅包含 subKeys 键,因此条件始终为真,您将用空白字典替换先前添加的字典。

【讨论】:

  • 我已经编辑了评论。尝试复制代码并放入python文件并自己运行。
  • 谢谢。这些更改解决了我遇到的问题。
猜你喜欢
  • 2021-06-02
  • 2018-11-16
  • 1970-01-01
  • 2021-07-16
  • 2011-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多