我通过道格·海尔曼的“Python标准库的示例”工作和整个这个传来:编译在Python
“1.3.2编译表达式 再包括模块级功能使用正则表达式作为文本字符串,但编译程序使用频繁的表达式更有效。“
我无法按照他的解释为什么这是这种情况。他表示,“模块级函数维护一个编译表达式的缓存”,并且由于“缓存的大小”是有限的,“使用编译表达式直接避免了缓存查找开销。”
如果有人能够请解释或指导我解释一下,我可以更好地理解为什么编译程序常用的正则表达式以及该过程如何实际工作更有效率,我将不胜感激。
我通过道格·海尔曼的“Python标准库的示例”工作和整个这个传来:编译在Python
“1.3.2编译表达式 再包括模块级功能使用正则表达式作为文本字符串,但编译程序使用频繁的表达式更有效。“
我无法按照他的解释为什么这是这种情况。他表示,“模块级函数维护一个编译表达式的缓存”,并且由于“缓存的大小”是有限的,“使用编译表达式直接避免了缓存查找开销。”
如果有人能够请解释或指导我解释一下,我可以更好地理解为什么编译程序常用的正则表达式以及该过程如何实际工作更有效率,我将不胜感激。
嗯。这很奇怪。我的知识至今(上涨,以及其他来源,from this question)建议我最初的回答:
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
我相信他想说的是你不应该在你的循环中编译你的正则表达式,而是在它之外。然后您可以在循环内运行已编译的代码。
代替:
while true:
result = re.match('A', str)
你应该把:
regex = re.compile('A')
while true:
result = regex.match(str)
基本上
re.match(pattern, str)
结合了编译和匹配步骤。在循环内部编译相同的模式效率低下,因此应该在循环之外提升。
查看Tim的正确推理的答案。
这是不正确的。 Python编译每个正则表达式并缓存它们以供以后重用,所以它们在每次迭代中都不重新编译。通过预编译正则表达式,您唯一可以避免的是每次迭代的缓存查找。 –
@TimPietzcker我刚刚开始研究Python,所以我想我知道我在说什么。谢谢你纠正我。 – Michael
我只是做了一些测试,看来我的回答并不完全正确。我会进一步研究。 –
这听起来我像笔者简单地说这是更高效的编译正则表达式并保存比来算在它之前编译的版本上仍然保存在模块的有限尺寸的内部缓存中。这可能是因为编译它们所花费的工作量以及必须首先发生的额外缓存查找开销大于客户端仅仅存储它们本身。
谢谢蒂姆!我把这个和你新发布的关于这个问题的问题联系起来了。这非常有用 - 非常感谢! – user7186