访问 Match.group() 的成本是多少?

How expensive is accessing Match.group()?

试图优化一些重用匹配组的代码,我想知道访问 Match.group() 是否很昂贵。我试图挖掘 re.py 的源代码,但代码有点神秘。

一些测试似乎表明将 Match.group() 的输出存储在变量中可能会更好,但我想了解调用 Match.group() 时到底发生了什么,如果还有另一种内部方式可以直接访问组的内容。

一些示例代码来说明潜在用途:

import re

m = re.search('X+', f'__{"X"*10000}__')

# do something
# m.group()

# do something else
# m.group()
时间

直接访问:

%%timeit
len(m.group())
220 ns ± 1.31 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

中间变量:

X = m.group()
%%timeit
len(X)
# 51 ns ± 0.172 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

参考文献:
current re.py code (python 3.10)
current sre_compile.py code (python 3.10)

消除属性访问的影响(变化不大)

G = m.group

%%timeit
len(G())
230 ns ± 1.12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

匹配对象包含对您搜索的原始字符串的引用,以及每个组开始和结束的索引,包括组 0,整个匹配字符串。每次调用 group() 都会将原始字符串切片以创建一个新字符串 return.

将 return 值保存到一个变量中,避免了每次都必须对字符串进行切片的时间和内存成本。 (它也避免了重复方法调用的开销。)

您可以看到 group() 不仅仅是 return 缓存的字符串,因为 return 值并不总是同一个对象:

>>> import re
>>> x = re.search(r'sd', 'asdf')
>>> x.group() is x.group()
False

如果想看group()的实现,在Python源码Modules/_sre.c中的match_group

.group 可用于访问整个匹配项(当未提供参数时)或特定组(当给出参数时),例如

import re
m = re.match('(X)X+','XXXXX')
print(m.group(1)) # output X

请注意,re.Match 个实例有 .string,您可以访问完整的字符串,即

import re
m = re.match('(X)X+','XXXXX')
print(m.string) # output XXXXX