2013-10-18 81 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次。