【问题标题】:Loading values to nested dicts from a list将值加载到列表中的嵌套字典
【发布时间】:2018-06-20 16:23:20
【问题描述】:

我有一个 CSV 文件,其标题如下:

cpus/0/compatible   clocks/HSE/compatible   ../frequency    memories/flash/compatible   ../address  ../size [and so on...]

我能够将该标题解析为嵌套字典,如下所示:

{'clocks': {'HSE': {'compatible': '[1]',
                    'frequency': '[2]'}},
 'cpus': {'0': {'compatible': '[0]'}},
 'memories': {'bkpsram': {'address': '[13]',
                          'compatible': '[12]',
                          'size': '[14]'},
              'ccm': {'address': '[7]',
                      'compatible': '[6]',
                      'size': '[8]'},
              'flash': {'address': '[4]',
                        'compatible': '[3]',
                        'size': '[5]'},
              'sram': {'address': '[10]',
                       'compatible': '[9]',
                       'size': '[11]'}},
 'pin-controller': {'GPIOA': {'enabled': '[16]'},
                    'GPIOB': {'enabled': '[17]'},
                    'GPIOC': {'enabled': '[18]'},
                    'GPIOD': {'enabled': '[19]'},
                    'GPIOE': {'enabled': '[20]'},
                    'GPIOF': {'enabled': '[21]'},
                    'GPIOG': {'enabled': '[22]'},
                    'GPIOH': {'enabled': '[23]'},
                    'GPIOI': {'enabled': '[24]'},
                    'GPIOJ': {'enabled': '[25]'},
                    'GPIOK': {'enabled': '[26]'},
                    'compatible': '[15]'}}

(它是一个dict 对象,用pprint() 打印)

类似于'[<number>]' 的键的值反映了 CSV 文件中应该从中加载数据的列的索引。

由于我主要使用 C/C++,我实际上很想在 Python 中使用指针/引用,因为那时我只需在每个值中放置一个指向列表元素的指针,并且对于每一行我可以修改列表内容,但我认为在 Python 中没有办法轻易获得这种行为。

所以现在我打算将这个字典转储到一个字符串中,并连续执行以下 3 次修改:

  • {替换为{{
  • }替换为}}
  • '[<number>]' 替换为{<number>}

之后,我将能够使用类似 ast.literal_eval(dictAsStr.format(*rowFromCsv)) 的内容“加载”数据,但是将整个字典转换为字符串然后再转换回字典似乎是浪费时间......

我在这里错过了其他一些明显的解决方案吗? CSV 的格式和我加载标题的方式不是固定的,我可以很容易地改变它,但是 我真的想要一个不会归结为“递归访问每个键并从当前行加载适当的值”的解决方案手动”

从 CSV 文件中,我将每一行加载为字符串列表,例如:

['["ARM,Cortex-M4", "ARM,ARMv7-M"]',
 '["ST,STM32-HSE", "fixed-clock"]',
 '0',
 '["on-chip-flash"]',
 '0x8000000',
 '131072',
 '',
 '',
 '',
 '["on-chip-ram"]',
 '0x20000000',
 '65536',
 '',
 '',
 '',
 '["ST,STM32-GPIOv2-pin-controller"]',
 'False',
 'False',
 'False',
 '',
 '',
 '',
 '',
 'False',
 '',
 '',
 '']

现在我想将每个加载的行(字符串列表)中的值插入到嵌套字典中的适当键中,因此按照上面的示例,我想得到:

{'clocks': {'HSE': {'compatible': '["ST,STM32-HSE", "fixed-clock"]',
                    'frequency': '0'}},
 'cpus': {'0': {'compatible': '["ARM,Cortex-M4", "ARM,ARMv7-M"]'}},
 'memories': {'bkpsram': {'address': '',
                          'compatible': '',
                          'size': ''},
              'ccm': {'address': '',
                      'compatible': '',
                      'size': ''},
              'flash': {'address': '0x8000000',
                        'compatible': '["on-chip-flash"]',
                        'size': '131072'},
              'sram': {'address': '0x20000000',
                       'compatible': '["on-chip-ram"]',
                       'size': '65536'}},
 'pin-controller': {'GPIOA': {'enabled': 'False'},
                    'GPIOB': {'enabled': 'False'},
                    'GPIOC': {'enabled': 'False'},
                    'GPIOD': {'enabled': ''},
                    'GPIOE': {'enabled': ''},
                    'GPIOF': {'enabled': ''},
                    'GPIOG': {'enabled': ''},
                    'GPIOH': {'enabled': 'False'},
                    'GPIOI': {'enabled': ''},
                    'GPIOJ': {'enabled': ''},
                    'GPIOK': {'enabled': ''},
                    'compatible': '["ST,STM32-GPIOv2-pin-controller"]'}}

为了完整起见,这里是我要加载的 CSV 文件的前几行。第一列不是上面介绍的字典的一部分,因为它用于索引。

chip,cpus/0/compatible,clocks/HSE/compatible,../frequency,memories/flash/compatible,../address,../size,memories/ccm/compatible,../address,../size,memories/sram/compatible,../address,../size,memories/bkpsram/compatible,../address,../size,pin-controller/compatible,pin-controller/GPIOA/enabled,pin-controller/GPIOB/enabled,pin-controller/GPIOC/enabled,pin-controller/GPIOD/enabled,pin-controller/GPIOE/enabled,pin-controller/GPIOF/enabled,pin-controller/GPIOG/enabled,pin-controller/GPIOH/enabled,pin-controller/GPIOI/enabled,pin-controller/GPIOJ/enabled,pin-controller/GPIOK/enabled
STM32F401CB,"[""ARM,Cortex-M4"", ""ARM,ARMv7-M""]","[""ST,STM32-HSE"", ""fixed-clock""]",0,"[""on-chip-flash""]",0x8000000,131072,,,,"[""on-chip-ram""]",0x20000000,65536,,,,"[""ST,STM32-GPIOv2-pin-controller""]",False,False,False,,,,,False,,,
STM32F401CC,"[""ARM,Cortex-M4"", ""ARM,ARMv7-M""]","[""ST,STM32-HSE"", ""fixed-clock""]",0,"[""on-chip-flash""]",0x8000000,262144,,,,"[""on-chip-ram""]",0x20000000,65536,,,,"[""ST,STM32-GPIOv2-pin-controller""]",False,False,False,,,,,False,,,
STM32F401CD,"[""ARM,Cortex-M4"", ""ARM,ARMv7-M""]","[""ST,STM32-HSE"", ""fixed-clock""]",0,"[""on-chip-flash""]",0x8000000,393216,,,,"[""on-chip-ram""]",0x20000000,98304,,,,"[""ST,STM32-GPIOv2-pin-controller""]",False,False,False,,,,,False,,,

用于解析头部的代码:

import csv

with open("some-path-to-CSV-file") as csvFile:
    csvReader = csv.reader(csvFile)
    header = next(csvReader)
    previousKeyElements = header[1].split('/')
    dictionary = {}
    for index, key in enumerate(header[1:]):
        keyElements = key.split('/')
        i = 0
        while keyElements[i] == '..':
            i += 1
        keyElements[0:i] = previousKeyElements[0:-i]
        previousKeyElements = keyElements
        node = dictionary
        for keyElement in keyElements[:-1]:
            node = node.setdefault(keyElement, {})
        node[keyElements[-1]] = '[{}]'.format(index)

【问题讨论】:

  • 你能展示你的示例输出吗?
  • @Rakesh - 你有什么示例输出?没有可用的最终输出,因为我仍然想知道如何很好地加载它,但是 - 为简单起见 - CSV 中的值可以被认为只是字符串。因此,我正在寻找一种方法,将字典值中的“标签”替换为从 CSV 文件中的后续行加载的实际字符串。稍后我将粘贴几行 CSV 文件,但是当作为文本查看时,这真的很“密集”...
  • "由于我主要使用 C/C++,我实际上很想在 Python 中使用指针/引用,因为我只需在每个值中放置一个指向列表元素的指针,并且对于每一行我可以修改列出内容,但我认为在 Python 中无法轻松获得这种行为。” => 我不清楚您在这里要做什么,但是如果您解释了您要解决的 real 问题,而不是您认为的解决方案,我们可能会建议使用 pythonic解决方案。
  • @brunodesthuilliers - 是的,我正在寻找 Pythonic 解决方案。我只是描述了我想到的解决方案,仅此而已。问题是“将字符串列表中的值(这是我从 CSV 行中得到的)加载到嵌套字典(这是我最终想要的)中的干净方法,可能不需要递归访问每个键并加载手动值(因为这是一个明显的解决方案 (; )"。
  • @FreddieChopin 是的,我明白这一点 - 我不明白你真正想要做什么(你想要解决什么问题)。您能否至少为您发布的 csv 提取添加一个预期的最终输出示例?

标签: python csv dictionary nested mapping


【解决方案1】:

仅使用实际行索引(作为整数)作为“已解析”标头中的值,即:

{'clocks': {'HSE': {'compatible': 1,
                'frequency': 2}},
# etc

然后在解析的标题副本上使用递归从行值填充它?:

import csv
import sys
import copy
import pprint

def parse_header(header):
    previousKeyElements = header[1].split('/')
    dictionary = {}
    for index, key in enumerate(header[1:]):
        keyElements = key.split('/')
        i = 0
        while keyElements[i] == '..':
            i += 1
        keyElements[0:i] = previousKeyElements[0:-i]
        previousKeyElements = keyElements
        node = dictionary
        for keyElement in keyElements[:-1]:
            node = node.setdefault(keyElement, {})
        node[keyElements[-1]] = index
    return dictionary

def _rparse(d, k, v, row):
    if isinstance(v, dict):
        for subk, subv in v.items():
            _rparse(v, subk, subv, row)
    elif isinstance(v, int):
        d[k] = row[v]
    else:
        raise ValueError("'v' should be either a dict or an int (got : %s(%s))" % (type(v), v))


def parse_row(header, row):
    struct = copy.deepcopy(header)
    for k, v in struct.items():
        _rparse(struct, k, v, row)
    return struct

def main(*args):
    path = args[0]
    with open(path) as f:
        reader = csv.reader(f)
        header = parse_header(next(reader))
        results = [parse_row(header, row[1:]) for row in reader]

    pprint.pprint(results)


if __name__ == "__main__":
    main(*sys.argv[1:])

另一个解决方案(实际上可能更快)是构建一个反向映射,其中行索引作为键,dict“路径”作为值,即:

{0: ("cpus", "0", "compatible"),
 1: ("clocks", "HSE", "compatible"),
 2: ("clocks", "HSE", "frequency"),
 # etc
}

然后:

def parse_row(template, map, row):
   # 'template' is your parsed header dict
   struct = copy.deepcopy(template)
   target = struct  
   for index, path in map.items():
       for key in path[:-1]:
           target = target[key]
       target[key[-1] = row[index]

哦,是的,作为额外的奖励,您可能希望使用 ast.literal_eval() 将您的值转换为正确的 python 类型:

>>> import ast
>>> ast.literal_eval("False")
False
>>> ast.literal_eval('["on-chip-flash"]')
['on-chip-flash']
>>> ast.literal_eval('0x8000000')
134217728
>>> ast.literal_eval('["ARM,Cortex-M4", "ARM,ARMv7-M"]')
['ARM,Cortex-M4', 'ARM,ARMv7-M']
>>> ast.literal_eval("this should fail")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/ast.py", line 49, in literal_eval
    node_or_string = parse(node_or_string, mode='eval')
  File "/usr/lib/python2.7/ast.py", line 37, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "<unknown>", line 1
    this should fail
              ^
SyntaxError: invalid syntax


>>> def to_python(value):
...     try:
...         return ast.literal_eval(value)
...     except Exception as e:
...         return value
... 
>>> to_python('["on-chip-flash"]')
['on-chip-flash']
>>> to_python('wtf')
'wtf'
>>> 

【讨论】:

  • 第一个解决方案显然属于“递归访问每个键并手动从当前行加载适当的值”类别,第二个解决方案很有趣,因为它是“反转的”。
  • Err,抱歉,我实际上错过了您帖子中关于您希望避免“递归访问每个键”解决方案的部分。
猜你喜欢
  • 2021-01-26
  • 2017-06-17
  • 1970-01-01
  • 1970-01-01
  • 2021-06-08
  • 2020-06-01
  • 1970-01-01
  • 2019-09-20
  • 1970-01-01
相关资源
最近更新 更多