在Python中缓存已编译的正则表达式对象?
每次导入一个包含大量静态正则表达式的Python文件时,计算机的处理器都会花时间把这些字符串编译成内存中的状态机。
a = re.compile("a.*b")
b = re.compile("c.*d")
...
问题是:有没有办法把这些正则表达式预先编译好,存储在磁盘的缓存中,这样每次导入时就不需要再执行编译了?
把对象进行序列化(也就是“打包”)其实只是做了以下操作,这样编译还是会发生:
>>> import pickle
>>> import re
>>> x = re.compile(".*")
>>> pickle.dumps(x)
"cre\n_compile\np0\n(S'.*'\np1\nI0\ntp2\nRp3\n."
而且,re
对象是不能被反序列化的:
>>> import marshal
>>> import re
>>> x = re.compile(".*")
>>> marshal.dumps(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: unmarshallable object
6 个回答
首先,这个问题是Python的re模块本身的一个限制。它规定了正则表达式的大小和复杂度,不能太大。对于长时间运行的程序,这个限制会相对宽松,而对于像命令行应用这样的短暂程序,这个限制就会比较严格。
几年前我研究过这个问题,发现可以把编译后的结果提取出来,保存成一个文件,然后再读取回来使用。问题是,这样做需要用到sre.py这个内部文件,所以在不同的Python版本中可能不太适用。
我希望能在我的工具箱里有这样的功能。同时,我也想知道有没有其他的模块可以替代这个功能。
请注意,每个模块在应用程序运行期间只会初始化一次,无论你导入它多少次。所以如果你在模块的全局范围内编译你的表达式(也就是说,不是在某个函数里面),这样做是没问题的。
有没有可能把这些正则表达式存储在磁盘的缓存中,以预编译的方式,避免每次导入时都要执行正则表达式的编译?
这不太容易。你需要写一个自定义的序列化程序,这个程序要和Python正则表达式引擎的C语言实现部分(叫做sre
)连接在一起。这样做的性能提升可能会被所需的时间和精力大大抵消。
首先,你有没有实际分析过代码的性能?我怀疑编译正则表达式在你应用程序的运行时间中占的比重并不大。记住,正则表达式只会在当前执行中第一次导入模块时编译,之后这个模块和它的属性会被缓存到内存中。
如果你的程序基本上是一次性运行,编译一堆正则表达式,然后退出,你可以尝试重新设计它,让它在一次调用中执行多个测试。这样你就可以像上面提到的那样重用这些正则表达式。
最后,你可以把正则表达式编译成基于C的状态机,然后通过扩展模块把它们链接进来。虽然这样可能会更难维护,但可以完全消除你应用程序中的正则表达式编译过程。