Python:在哪里编译RE?
我有一个函数,它通过使用正则表达式查找信息来迭代文本文件以提取某些信息。但是,程序会遍历多个文件,因此会多次调用此函数 目前,我已将正则表达式编译为函数的第一步。但我开始怀疑,从编程的角度来看,这是否是一个好的设计,因为函数会被多次调用 解释器是否足够聪明,可以看到它们不会改变,并在运行之间缓存它们?或者,我考虑将它们编译为全局变量,以便它始终只能编译一次,但这会将正则表达式与其使用位置分开,从而使其更难读取。我看到的另一个选项是将函数创建为闭包,并在创建时传递正则表达式值,但这似乎不必要地复杂 简言之,编译RE(或任何其他计算过一次的值)的最有效方法是什么?它仍然是可读的和pythonic的?Python:在哪里编译RE?,python,performance,Python,Performance,我有一个函数,它通过使用正则表达式查找信息来迭代文本文件以提取某些信息。但是,程序会遍历多个文件,因此会多次调用此函数 目前,我已将正则表达式编译为函数的第一步。但我开始怀疑,从编程的角度来看,这是否是一个好的设计,因为函数会被多次调用 解释器是否足够聪明,可以看到它们不会改变,并在运行之间缓存它们?或者,我考虑将它们编译为全局变量,以便它始终只能编译一次,但这会将正则表达式与其使用位置分开,从而使其更难读取。我看到的另一个选项是将函数创建为闭包,并在创建时传递正则表达式值,但这似乎不必要地复杂
谢谢。Python的正则表达式模块确实缓存了最近使用的正则表达式的编译版本,因此您可以删除显式编译,而不会出现问题。您可以编写一个简单的
RegexCache
类。类似于(未经测试):
然后,而不是:
re.search(complex_pattern, whatver)
你可以:
re.search(recache.get(complex_pattern), whatver)
这将使模式与使用它的位置保持一致,同时也确保您多次使用的模式将被编译。您还必须确保缓存不会变得太大。必须注意:在进入这个兔子洞之前,始终确保这是您的瓶颈。如果只运行正则表达式几次,并且正则表达式不是特别复杂,那么节省的时间可能很小 如果计划多次运行正则表达式操作,最好使用
re.compile
。然而,如果没有更多细节,最多可以说的是:您应该显式地进行测试。使用timeit或其他模块为流程计时
至于缓存已编译的正则表达式,无论它是否在后台缓存,如果直接使用re函数而不是先编译,都将付出代价。要查看这一点,您应该使用dis
查看它的功能:
>>> def f():
... x="foo bar baz"
... return re.match("foo", x)
...
>>> dis.dis(f)
2 0 LOAD_CONST 1 ('foo bar baz')
3 STORE_FAST 0 (x)
3 6 LOAD_GLOBAL 0 (re)
9 LOAD_ATTR 1 (match)
12 LOAD_CONST 2 ('foo') ** always have to pass the regex
15 LOAD_FAST 0 (x)
18 CALL_FUNCTION 2
21 RETURN_VALUE
>>> n=re.compile("foo")
>>> def g():
... x="foo bar baz"
... return n.match("foo")
...
>>> dis.dis(g)
2 0 LOAD_CONST 1 ('foo bar baz')
3 STORE_FAST 0 (x)
3 6 LOAD_GLOBAL 0 (n)
9 LOAD_ATTR 1 (match)
12 LOAD_FAST 0 (x)
15 CALL_FUNCTION 1
18 RETURN_VALUE
因此,即使它在后台缓存正则表达式,计划也必须将正则表达式传递给
re.match
(这是不可避免的)。编译后的版本避免了这一步。将re编译并作为模块级对象使用没有什么错。并不是说它始终是当前的最佳实践,而是指在stdlib的各个模块中使用的
对我来说,使用模块级编译的re对象比3.3的缓存更可取,因为缓存依赖于您可能无法控制(或在未来版本中可能会更改)的实现细节。在模块范围内定义它们可以让代码的读者清楚地看到,它们只编译了一次,使用了N次。您有相关的源代码吗?@AmirRachum:有。默认情况下,Python 3.3中最多会缓存512个最后的模式,请参阅。谢谢。听起来,除非使用了大量的模式,否则信任缓存是一条可行之路。我相信这是一种过早优化的情况。在你的情况下,做你觉得最容易理解的事情。如果您有性能问题,请运行探查器,看看问题出在哪里。@AmirRachum在这种情况下,您可能是对的。但是,这不是我第一次或最后一次有这样的设计,所以很高兴尽早将其正确,至少在不损害可读性的情况下;你只是增加了更多的开销。@Amber:也许,这取决于你使用了多少正则表达式。还有一种说法,显式比隐式好。此外,如果超过python缓存大小,它只会立即清除所有内容,因此如果您说
cache\u size+1
regex是一行使用的,那么它不会缓存任何内容。由于python会自动缓存最近的regex,因此,如果要大量运行,您只需要显式编译(想想数百个)重复使用不同的正则表达式,每次多次。
>>> def f():
... x="foo bar baz"
... return re.match("foo", x)
...
>>> dis.dis(f)
2 0 LOAD_CONST 1 ('foo bar baz')
3 STORE_FAST 0 (x)
3 6 LOAD_GLOBAL 0 (re)
9 LOAD_ATTR 1 (match)
12 LOAD_CONST 2 ('foo') ** always have to pass the regex
15 LOAD_FAST 0 (x)
18 CALL_FUNCTION 2
21 RETURN_VALUE
>>> n=re.compile("foo")
>>> def g():
... x="foo bar baz"
... return n.match("foo")
...
>>> dis.dis(g)
2 0 LOAD_CONST 1 ('foo bar baz')
3 STORE_FAST 0 (x)
3 6 LOAD_GLOBAL 0 (n)
9 LOAD_ATTR 1 (match)
12 LOAD_FAST 0 (x)
15 CALL_FUNCTION 1
18 RETURN_VALUE