无法使pyparsing Dict()返回嵌套字典

13

我正在尝试解析以下形式的字符串:

'foo(bar:baz;x:y)'
我希望结果以嵌套字典的形式返回,即对于上述字符串,结果应该如下所示:
{ 'foo' : { 'bar' : 'baz', 'x' : 'y' } }

尽管我使用了多种Dict()和Group()的组合,但仍然无法使其工作。我的语法(其中一种版本)如下:

import pyparsing as pp
field_name = pp.Word( pp.alphanums )
field_value = pp.Word( pp.alphanums )
colon = pp.Suppress( pp.Literal( ':' ) )

expr = pp.Dict( 
    pp.Group( 
        field_name + \
        pp.nestedExpr( 
            content = pp.delimitedList( 
                 pp.Group( field_name + colon + field_value ), 
                 delim = ';' 
            ) 
        ) 
    ) 
)

现在,结果如下所示:

In [62]: str = 'foo(bar:baz;x:y)'

In [63]: expr.parseString( str ).asList()
Out[63]: [['foo', [['bar', 'baz'], ['x', 'y']]]]

In [64]: expr.parseString( str ).asDict()
Out[64]: {'foo': ([(['bar', 'baz'], {}), (['x', 'y'], {})], {})}

In [65]: print( expr.parseString( str ).dump() )
Out[65]: [['foo', [['bar', 'baz'], ['x', 'y']]]]
         - foo: [['bar', 'baz'], ['x', 'y']]

所以我认为asList()版本看起来很不错,应该能得到我想要的字典。当然,鉴于(就我理解而言,请纠正我如果我错了)Dict()将通过使用列表的第一个元素作为键,并将其余所有元素作为该键的值来解析令牌列表,这在某种程度上可以实现非嵌套字典。例如,在这种情况下:

expr = pp.Dict( 
    pp.delimitedList( 
        pp.Group( field_name + colon + field_value ), 
        delim = ';' 
    ) 
)

In [76]: expr.parseString( 'foo:bar;baz:x' ).asDict()
Out[76]: {'baz': 'x', 'foo': 'bar'}
所以,问题是第一种情况有什么问题(以及我对问题的理解),或者可能是 Dict()无法处理这种情况?我可以使用 asList() 并将其手动转换为字典,但我宁愿让 pyparsing 来完成它 :)
非常感谢任何帮助或指导。
谢谢。

OBE - 截至pyparsing 2.1.0(2016年2月),asDict()也将对嵌套的ParseResults进行字典化。 - PaulMcG
1个回答

9

两个问题:

  • 你需要在pp.delimitedList周围添加pp.Dict以使内部结果上的asDict正确工作
  • 你只调用了最外层ParsingResult实例上的asDict,让内部ParsingResult“未被解释”

我尝试了以下方法:

from pyparsing import *
field_name = field_val = Word(alphanums)
colon = Suppress(Literal(':'))

expr = Dict(Group(
    field_name +
    nestedExpr(content =
        Dict(delimitedList( 
            Group(field_name + colon + field_value), 
            delim = ';' 
        ))
    )
))

然后像这样使用它:
>>> res = expr.parseString('foo(bar:baz;x:y)')
>>> type(res['foo'])
<class 'pyparsing.ParseResults'>
>>> { k:v.asDict() for k,v in res.asDict().items() }
{'foo': {'x': 'y', 'bar': 'baz'}}

漏掉 pp.Dict 的发现很好。另外,尝试打印 res.dump() 以查看嵌套的键和值。(由于 res 是一个 ParseResults 对象,它将支持嵌套的字典样式访问,而无需使用 asDict 进行转换: res['foo']['x'] 给出 'y'; 或者您可以使用点属性表示法,只要键是良好的 Python 标识符: res.foo.bar 给出 'baz'。) - PaulMcG
嗨@Paul,从作者本人那里收到赞美感觉很好 :) 我发现res.dump()并没有比str(res)更有信息量,但也许我只是不知道如何解释它?我以前从未使用过pyparsing。 - Niklas B.
非常感谢你,Niklas!我之前不知道结果中也包含ParseResults实例,我以为它们已经是列表或字典了。Paul - 感谢你提供的使用as dict而无需转换的技巧,这对我正在处理的工作可能会很有用! :) - kgr
也花了我一点时间才弄明白,因为字符串表示与Python数据结构相同。@Paul:为什么表示不包括有关类型的提示呢?我想那将是一个有用的功能 :) - Niklas B.
res.dump() 应该显示嵌套的令牌列表,然后是任何命名结果的缩进树形结构 - 对于这个简单的示例应该相当直接。ParseResults 的 str() 表示显示令牌列表,然后是命名结果的 dict-style repr,作为一个元组全部在一起。实际上,我觉得 str() 输出有点丑陋,更喜欢 dump()。至于如何显示某种类型的指示,由于嵌套结果的存在,它变得更加丑陋,因为正如您发现的那样,外部对象是 ParseResults,所有内部嵌套对象也是。 - PaulMcG
OBE - 截至 pyparsing 2.1.0(2016 年 2 月),asDict() 将对嵌套的 ParseResults 进行字典化。 - PaulMcG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接