【问题标题】:How expensive is accessing Match.group()?访问 Match.group() 的成本是多少?
【发布时间】:2022-01-27 19:22:34
【问题描述】:

试图优化一些重用匹配组的代码,我想知道访问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)

【问题讨论】:

  • 您确定这与Match.group() 直接相关,而不仅仅是Why is local variable access faster than class member access 的情况吗?
  • 另外,我没有彻底检查re 的代码,但如果group() 是一个函数/方法,那么多次调用它的开销将超过仅仅调用一次。更好的测试是保存对它的引用 (x = match.group),然后对 x() 的调用计时。
  • @DeepSpace 不错的建议,我试过了,它并没有改变太多时间

标签: python regex optimization


【解决方案1】:

匹配对象包含对您搜索的原始字符串的引用,并索引每个组的开始和结束位置,包括第 0 组,即整个匹配的字符串。每次调用 group() 都会对原始字符串进行切片以创建一个新字符串以返回。

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

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

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

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

【讨论】:

  • 感谢您的输入,如果每次都对字符串进行切片是有道理的!我只是找不到它在源中发生的位置
【解决方案2】:

.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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-20
    • 2013-02-11
    相关资源
    最近更新 更多