为什么pyparsing的`DelimitedList`和`Dict`一起使用这么别扭?

问题描述 投票:0回答:1

Pyparsing 提供

ParseElementEnhance
子类
DelimitedList
用于解析(通常以逗号分隔)列表:

>>> kv_element = pp.Word(pp.alphanums)
>>> kv_list = pp.DelimitedList(kv_element)
>>> kv_list.parse_string('red, green, blue')
ParseResults(['red', 'green', 'blue'], {})

它提供了

TokenConverter
子类
Dict
,用于将重复表达式转换为字典:

>>> key = value = pp.Word(pp.alphanums)
>>> kv_pair = key + pp.Suppress("=") + value
>>> kv_dict = pp.Dict(pp.Group(kv_pair)[...])
>>> kv_dict.parse_string('R=red G=green B=blue')
ParseResults([
  ParseResults(['R', 'red'], {}),
  ParseResults(['G', 'green'], {}),
  ParseResults(['B', 'blue'], {})
], {'R': 'red', 'G': 'green', 'B': 'blue'})

但是将它们结合起来感觉很尴尬。可以构建一个成功的组合

ParserElement
来从分隔列表中解析字典,但与上面的相比,它需要:

  1. 重新定义
    DelimitedList
    来输出
    Group()
    s
  2. 在其周围构建
    DelimitedList
    时重复
    Dict()
    ,以安抚类型检查器。1
>>> kv_pair = key + pp.Suppress("=") + value
>>> kv_pairlist = pp.DelimitedList(pp.Group(kv_pair))
>>> kv_pairdict = pp.Dict(kv_pairlist[...])
>>> kv_pairdict.parse_string('R=red, G=green, B=blue')
ParseResults([
  ParseResults(['R', 'red'], {}), 
  ParseResults(['G', 'green'], {}), 
  ParseResults(['B', 'blue'], {})
], {'R': 'red', 'G': 'green', 'B': 'blue'})

整个效果读起来就像您正在定义一个解析器,以从 series 的 1 元素分隔列表创建字典,每个列表包含一个键值对匹配。 (事实上,我并不完全确定这不是解析器中实际发生的事情。)

编写代码来表达意图——解析器定义来匹配单个分隔列表,包含一系列键值对匹配——感觉就像是在与 API 作斗争。 (事实上,使用

kv_pairdict = pp.Dict(kv_pairlist)

function 与上面相同,但与类型检查器发生冲突,这一点尤其令人烦恼。)

在 Pyparsing API 中是否有更清晰的方式来表达预期的解析器定义?如果不是,这是我的设计、Pyparsing API 的缺陷还是其他什么?

(我的定义是从里到外的吗?

DelimitedList(Dict(Group(kv_pair)[1, ...]))

也可以工作,但对我来说概念上更倒退。但它并没有涉及到与 API 的那么多对抗,所以也许我只是看错了.)

注释

    (否则,至少在 VSCode 中,它会得到这个听起来有点疯狂的注释:)
  1. “dict”的重载变体与参数类型不匹配 “DelimitedList”(mypycall 重载)

    可能的过载变体:

    def [_KT, _VT] __init__(self) -> dict[_KT, _VT] def [_KT, _VT] __init__(self, **kwargs: _VT) -> dict[str, _VT] def [_KT, _VT] __init__(self, SupportsKeysAndGetItem[_KT, _VT], /) -> dict[_KT, _VT] def [_KT, _VT] __init__(self, SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT) -> dict[str, _VT] def [_KT, _VT] __init__(self, Iterable[tuple[_KT, _VT]], /) -> dict[_KT, _VT] def [_KT, _VT] __init__(self, Iterable[tuple[str, _VT]], /, **kwargs: _VT) -> dict[str, _VT] def [_KT, _VT] __init__(self, Iterable[list[str]], /) -> dict[str, str] def [_KT, _VT] __init__(self, Iterable[list[bytes]], /) -> dict[bytes, bytes]mypy(note)
    
    
python parsing pyparsing
1个回答
0
投票

Dict

 将使用单个 
ParserElement
 来构造,它表示分组的 ParserElements 的重复,将第 0 个 Group 元素中匹配的文本作为返回的键,并将 Group 的其余部分作为值。通常,重复是使用 
OneOrMore
ZeroOrMore
(或其新的类似切片的符号 
[1, ...]
[...]
)完成的。但使用 
DelimitedList
 来进行这种重复是非常合适的,
只要用于创建它的表达式是一个 Group。看看对代码的轻微修改是否有帮助(我实际上只是将组移至 kv_pair
 定义):

key = pp.common.identifier # keep the keys usable for use as attribute names value = pp.Word(pp.alphanums) kv_pair = pp.Group(key + pp.Suppress("=") + value) kv_pairlist = pp.DelimitedList(kv_pair) kv_pairdict = pp.Dict(kv_pairlist) kv_pairdict.run_tests("""\ R=red, G=green, B=blue """ )
我将其保存为 

dict_of_delimited_list.py

,并添加这些行,我得到 
dict_of_delimited_list.html
,其中包含解析器的铁路图。

pp.autoname_elements() kv_pairdict.create_diagram(f"{__file__.removesuffix('.py')}_diagram.html")

Parser railroad diagram

至于你的注释,我强烈怀疑类型检查器存在问题。 Dict 的

__init__

 签名显然采用单个 ParserElement,而不是键类型和值类型,因此我怀疑类型检查器看到“pp.Dict”并思考“typing.Dict”。我使用 pyparsing 的修改版本将 
Dict
 重命名为 
DictOf
 确认了这一点,并且没有进行其他更改,并且您听起来疯狂的类型建议已得到解决。 (不幸的是,仅仅做 
from pyparsing import Dict as DictOf
 是不够的。)作为旁注,我想提一下 pyparsing 的 Dict 类早于打字。Dict 大约 15 年 - pyparsing 首先有 Dict!

© www.soinside.com 2019 - 2024. All rights reserved.