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
来从分隔列表中解析字典,但与上面的相比,它需要:
DelimitedList
来输出 Group()
sDelimitedList
时重复 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 的那么多对抗,所以也许我只是看错了.)注释
“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)
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")
至于你的注释,我强烈怀疑类型检查器存在问题。 Dict 的 __init__
签名显然采用单个 ParserElement,而不是键类型和值类型,因此我怀疑类型检查器看到“pp.Dict”并思考“typing.Dict”。我使用 pyparsing 的修改版本将
Dict
重命名为
DictOf
确认了这一点,并且没有进行其他更改,并且您听起来疯狂的类型建议已得到解决。 (不幸的是,仅仅做
from pyparsing import Dict as DictOf
是不够的。)作为旁注,我想提一下 pyparsing 的 Dict 类早于打字。Dict 大约 15 年 - pyparsing 首先有 Dict!