在Python中编译正则表达式
我正在阅读Doug Hellman的《Python标准库示例》,遇到了这样一段内容:
“1.3.2 编译表达式
re模块提供了一些函数,可以用来处理正则表达式和文本字符串,但如果一个程序经常使用某些表达式,编译这些表达式会更高效。”
我没能理解他为什么这么说。他提到“模块级函数会维护一个编译表达式的缓存”,而且因为“缓存的大小是有限的”,所以“直接使用编译好的表达式可以避免查找缓存的开销。”
如果有人能帮我解释一下,或者指引我找到更容易理解的解释,为什么频繁使用的正则表达式编译起来会更高效,以及这个过程是怎么运作的,我将非常感激。
3 个回答
我觉得作者的意思是,编译一个正则表达式(regex)并把它保存起来,比依赖模块内部有限的缓存中仍然保存着之前编译的版本要更有效率。这可能是因为编译正则表达式需要花费的时间和精力,加上查找缓存时的额外开销,远远超过客户端自己直接保存这些正则表达式的简单做法。
我觉得他想说的是,你不应该在循环里面编译你的正则表达式,而是应该在循环外面编译好。这样你就可以在循环里面直接运行已经编译好的代码。
而不是这样:
while true:
result = re.match('A', str)
你应该这样做:
regex = re.compile('A')
while true:
result = regex.match(str)
基本上,re.match(pattern, str)
是把编译和匹配这两个步骤合在一起了。在循环里面重复编译同一个模式是很低效的,所以应该把它放到循环外面。
可以参考Tim的回答,了解正确的理由。
嗯,这个有点奇怪。到目前为止,我的知识(部分来源于这个问题)让我得出了最初的答案:
初步答案
Python会缓存你最近使用的100个正则表达式,所以即使你没有明确地编译它们,每次使用时也不需要重新编译。
不过,这里有两个缺点:当你使用的正则表达式数量达到100个时,缓存会被清空。所以如果你连续使用101个不同的正则表达式,每一个都会在每次使用时被重新编译。虽然这种情况不太可能发生,但还是有这个风险。
第二,为了检查一个正则表达式是否已经被编译,解释器每次都需要在缓存中查找,这会花费一点额外的时间(不过时间不算长,因为查找字典的速度非常快)。
所以,如果你明确地编译你的正则表达式,就可以避免这个额外的查找步骤。
更新
我刚刚做了一些测试(Python 3.3):
>>> import timeit
>>> timeit.timeit(setup="import re", stmt='''r=re.compile(r"\w+")\nfor i in range(10):\n r.search(" jkdhf ")''')
18.547793477671938
>>> timeit.timeit(setup="import re", stmt='''for i in range(10):\n re.search(r"\w+"," jkdhf ")''')
106.47892003890324
所以看起来并没有进行缓存。也许这是因为timeit.timeit()
运行时的特殊条件?
另一方面,在Python 2.7中,差别就没有那么明显:
>>> import timeit
>>> timeit.timeit(setup="import re", stmt='''r=re.compile(r"\w+")\nfor i in range(10):\n r.search(" jkdhf ")''')
7.248294908492429
>>> timeit.timeit(setup="import re", stmt='''for i in range(10):\n re.search(r"\w+"," jkdhf ")''')
18.26713670282241