2013-10-18 84 views
2

我有一个函数遍历一个文本文件,通过使用正则表达式来提取某些信息来查找信息。但是,程序会覆盖多个文件,因此会多次调用此函数。Python:在哪里编译RE?

目前,我有正则表达式编译作为函数的第一步。但是我开始想知道,从编程的角度来看,这是否是好的设计,因为函数被调用了好几次。

解释器是否足够聪明,看到它们不会在运行之间改变并缓存它们?或者,我考虑将它们编译为全局变量,以便它始终可用,只能编译一次,但将正则表达式与将使用的地方分开,这会使其更难以阅读。我看到的另一个选项是创建函数作为闭包,并在创建时传入正则表达式值,但似乎不必要的复杂。

总之,什么是最有效的方法来编译的RE(或任何其他值计算一次)仍然可读和pythonic?

谢谢。

+5

我相信这是一个过早优化的情况。只要做你觉得在你的情况下最易读的东西。如果您遇到性能问题,请运行分析器并查看问题所在。 –

+0

@AirirRachum在这种特殊情况下,你可能是对的。但这不是我第一次也不是最后一次有这样的设计,所以它很高兴早日实现它,至少在不损害可读性的情况下。 – TimothyAWiseman

回答

5

Python的正则表达式模块缓存最近使用的正则表达式的编译版本,所以你可能会删除显式编译,并没有问题。

+0

你有这个来源吗? –

+7

@AirirRachum:是的。多达512个最后的模式默认在Python 3.3中缓存,请参阅[re.py](http://hg.python.org/cpython/file/28c25ae338fa/Lib/re.py#l266)。它是[Python 2.7中的100](http://hg.python.org/cpython/file/2.7/Lib/re.py#l224) – jfs

+0

太棒了。谢谢! –

0

你可以写一个简单的RegexCache类。喜欢的东西(未经测试):

class RegexCache(object): 
    def __init__(self): 
     self.cache = {} 

    def get(self, pattern): 
     if pattern not in self.cache: 
      self.cache[pattern] = re.compile(pattern) 

     return self.cache[pattern] 
recache = RegexCache() 

然后,而不是:

re.search(complex_pattern, whatver) 

你这样做:

re.search(recache.get(complex_pattern), whatver) 

这与它被用在共同保持模式尚未也保证了图案你多次使用将被编译。您还必须确保缓存不会太大。

+0

不要这样做 - Python已经为你缓存;你只是增加了更多的开销。 – Amber

+0

@Amber:也许,取决于你猜我使用了多少个正则表达式。还有显式比隐式更好。另外,如果你超过了python缓存大小,它就会立即清除整个事件,所以如果你有说'CACHE_SIZE + 1'正则表达式,你将在一行中使用它,它不会最终缓存任何东西。 – Claudiu

1

OBLIGATORY注意:在进入这个兔子洞之前,一定要确保这是你的瓶颈。如果您只运行正则表达式几次,并且如果正则表达式不是特别复杂,那么节省的时间可能很少。如果您计划多次运行正则表达式操作,则最好使用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(这是不可避免的)。编译后的版本避免了这一步骤。

+0

因为Python会自动缓存最近的正则表达式,所以如果你要反复运行很多次(想成百上千次)不同的正则表达式,那么你只需要真正地进行编译。 – Amber

0

将re编译并作为模块级对象提供没有什么问题。并不是说它总是当前的最佳实践,但它是stdlib中各种模块中使用的。

对我来说,使用模块级编译的重新对象比3.3的缓存更好,因为缓存依赖于您可能没有太多控制(或可能在未来版本中会更改)的实现细节。在模块范围内定义它们会使代码读者明白它们被编译了一次并使用了N次。