在 re.search 中重用已编译的正则表达式

Posted

技术标签:

【中文标题】在 re.search 中重用已编译的正则表达式【英文标题】:Reusing compiled regex in re.search 【发布时间】:2018-07-30 17:16:48 【问题描述】:

我偶然发现了以下代码:

import re 

regex_compiled = re.compile('\d2-\d3,5')

res = re.search(regex_compiled, '12-9876')

我的印象是 re.search 试图编译第一个已经编译的参数,所以它应该出错,或者应该在重复尝试编译之前调用 regex_compiled.__repr__()regex_compiled.__str__()

为了确定我将它与regex_compiled.search(...进行了比较:

>>> from timeit import timeit
>>> timeit("import re; regex_compiled = re.compile('\d2-\d3,5');     res = re.search(regex_compiled, '12-9876')")
1.3797054840251803

>>> timeit("import re; regex_compiled = re.compile('\d2-\d3,5');     res = regex_compiled.search('12-9876')")
0.7649686150252819
>>>

鉴于调试到 re.search(在 CPython v. 2 和 v. 3 中)显示 compiled 模式被重用,我很困惑从何而来!我希望有人可以帮助阐明这一点。

执行环境:Ubuntu 16.04、64b

【问题讨论】:

我不知道这是重复的。我完全了解缓存机制,这就是为什么我偶然发现时间差异很大的原因。这种差异让我很困扰。 【参考方案1】:

re._compile 首先检查参数是否被缓存,然后检查它是否已经编译。因此,当您将编译模式传递给re.whatever 时,它会浪费一些时间来计算和查找实际上永远不会匹配的缓存键。重新生成模式和OrderedDict 查找是繁重的操作,似乎可以解释您观察到的差异。

这种行为的一个可能原因是_compile 针对字符串模式进行了优化,这是它的主要用例,旨在尽快返回缓存命中。

以下是一些时间安排:

from time import time
import re
import sys

print(sys.version)

pat = '\d2-\d3,5'
loops = 1000000

re.purge()

t = time()
for _ in range(loops):
    re._compile(pat, 0)
print('compile string  ', time() - t)

re.purge()

rc = re._compile(pat, 0)
t = time()
for _ in range(loops):
    re._compile(rc, 0)
print('compile compiled', time() - t)

结果:

$ python3 test.py
3.5.2 (default, Nov 23 2017, 16:37:01) [GCC 5.4.0 20160609]
compile string   0.5387749671936035
compile compiled 0.7378756999969482

$ python2 test.py
2.7.12 (default, Nov 20 2017, 18:23:56) [GCC 5.4.0 20160609]
('compile string  ', 0.5074479579925537)
('compile compiled', 1.3561439514160156)

【讨论】:

以上是关于在 re.search 中重用已编译的正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

Python3中正则模块re.compilere.match及re.search函数

正则表达式的9个方法

Python中正则模块re.compilere.match及re.search函数用法

在 python 列表中使用正则表达式(re.search)

正则表达式中pattern.match(),re.match(),pattern.search(),re.search()方法的使用和区别

re正则表达式方法