Findall 返回命名捕获组的结果?

受到一个现在已经删除的问题的启发; 给定一个带有命名组的正则表达式,是否存在一个类似于 findall的方法,它返回一个带有命名捕获组的 dict列表,而不是一个 tuple列表?

给出:

>>> import re
>>> text = "bob sue jon richard harry"
>>> pat = re.compile('(?P<name>[a-z]+)\s+(?P<name2>[a-z]+)')
>>> pat.findall(text)
[('bob', 'sue'), ('jon', 'richard')]

相反,应该给予:

[{'name': 'bob', 'name2': 'sue'}, {'name': 'jon', 'name2': 'richard'}]
40664 次浏览

虽然没有这样做的内置方法,但是可以通过使用列表理解来实现预期的结果。

[dict([[k, i if isinstance(i, str) else i[v-1]] for k,v in pat.groupindex.items()]) for i in pat.findall(text)]

使用友好的格式:

>>> [
...     dict([
...         [k, i if isinstance(i, str) else i[v-1]]
...         for k,v in pat.groupindex.items()
...     ])
...     for i in pat.findall(text)
... ]

我们使用一个列表内涵构造一个列表,迭代来自 findall的结果,这个结果要么是一个字符串列表,要么是一个元组列表(0或1个捕获组导致一个 str列表)。

对于结果中的每个项目,我们从另一个列表内涵构建一个 dict,它是从编译模式的 groupindex字段生成的,看起来像:

>>> pat.groupindex
{'name2': 2, 'name': 1}

groupindex中的每个项目构造一个列表,如果来自 findall的项目是元组,则使用来自 groupindex的组号来查找正确的项目,否则该项目将被分配给(唯一现存的)命名组。

[k, i if isinstance(i, str) else i[v-1]]

最后,根据字符串列表构造一个 dict。

请注意,groupindex只包含命名的组,因此在生成的 dict中将省略未命名的捕获组。

结果就是:

[dict([[k, i if isinstance(i, str) else i[v-1]] for k,v in pat.groupindex.items()])  for i in pat.findall(text)]
[{'name2': 'sue', 'name': 'bob'}, {'name2': 'richard', 'name': 'jon'}]
>>> import re
>>> s = "bob sue jon richard harry"
>>> r = re.compile('(?P<name>[a-z]+)\s+(?P<name2>[a-z]+)')
>>> [m.groupdict() for m in r.finditer(s)]
[{'name2': 'sue', 'name': 'bob'}, {'name2': 'richard', 'name': 'jon'}]

你可以切换到 finditer

>>> import re
>>> text = "bob sue jon richard harry"
>>> pat = re.compile('(?P<name>[a-z]+)\s+(?P<name2>[a-z]+)')
>>> for m in pat.finditer(text):
...     print m.groupdict()
...
{'name2': 'sue', 'name': 'bob'}
{'name2': 'richard', 'name': 'jon'}

如使用 match:

r = re.match('(?P<name>[a-z]+)\s+(?P<name2>[a-z]+)', text)
r.groupdict()

文件