12

来自 Python 文档re.compile()

注意 传递给 re.match()、re.search() 或 re.compile() 的最新模式的编译版本被缓存,因此一次只使用几个正则表达式的程序不必担心编译正则表达式表达式。

然而,在我的测试中,这个断言似乎站不住脚。当重复使用相同模式的以下片段计时时,编译版本仍然比未编译版本快得多(应该被缓存)。

我在这里缺少什么来解释时差吗?

import timeit

setup = """
import re
pattern = "p.a.t.t.e.r.n"
target = "p1a2t3t4e5r6n"
r = re.compile(pattern)
"""

print "compiled:", \
    min(timeit.Timer("r.search(target)", setup).repeat(3, 5000000))
print "uncompiled:", \
    min(timeit.Timer("re.search(pattern, target)", setup).repeat(3, 5000000))

结果:

compiled: 2.26673030059
uncompiled: 6.15612802627
4

1 回答 1

13

这是(CPython)的实现re.search

def search(pattern, string, flags=0):
    """Scan through string looking for a match to the pattern, returning
    a match object, or None if no match was found."""
    return _compile(pattern, flags).search(string)

这是re.compile

def compile(pattern, flags=0):
    "Compile a regular expression pattern, returning a pattern object."
    return _compile(pattern, flags)

这依赖于re._compile

def _compile(*key):
    # internal: compile pattern
    cachekey = (type(key[0]),) + key
    p = _cache.get(cachekey)            #_cache is a dict.   
    if p is not None:
        return p
    pattern, flags = key
    if isinstance(pattern, _pattern_type):
        if flags:
            raise ValueError('Cannot process flags argument with a compiled pattern')
        return pattern 
    if not sre_compile.isstring(pattern):
        raise TypeError, "first argument must be string or compiled pattern"
    try:
        p = sre_compile.compile(pattern, flags)
    except error, v:
        raise error, v # invalid expression
    if len(_cache) >= _MAXCACHE:
        _cache.clear()
    _cache[cachekey] = p
    return p

所以你可以看到,只要正则表达式已经在字典中,唯一涉及的额外工作就是在字典中查找(这涉及创建一些临时元组,一些额外的函数调用......)。

更新 在过去的美好时光(上面复制的代码),缓存过大时会完全失效。这些天来,缓存循环——首先丢弃最旧的项目。此实现依赖于 python 字典的顺序(这是在 python3.7 之前的实现细节)。在 python3.6 之前的 Cpython 中,这会从缓存中删除一个任意值(可以说这仍然比使整个缓存无效更好)

于 2012-09-20T13:59:53.357 回答