2012-08-08 42 views
11

有沒有pythonic維護狀態的方法(例如爲了優化目的),而沒有完全面向對象?如何在沒有類的情況下在Python中維護狀態?

爲了說明我的問題比較好,這是我在JavaScript中經常使用模式的一個例子:

var someFunc = (function() { 
    var foo = some_expensive_initialization_operation(); 
    return someFunc (bar) { 
     // do something with foo and bar 
    } 
}()); 

外部講,這僅僅是一個函數像任何其他,而無需初始化對象之類的東西,但閉包允許計算單個時間的值,然後我將其作爲常量使用。

Python中的一個示例是優化正則表達式時 - 使用re.compile並存儲matchsearch操作的編譯版本很有用。

的唯一途徑,我知道在Python做到這一點是通過在模塊範圍內設置變量:

compiled_regex = compile_my_regex() 

def try_match(m): # In reality I wouldn't wrap it as pointlessly as this 
    return compiled_regex.match(m) 

,或者通過創建類:

class MatcherContainer(object): 
    def __init__(self): 
     self.compiled_regex = compile_my_regex() 
    def try_match(self, m): 
     self.compiled_regex.match(m) 

my_matcher = MatcherContainer() 

前一種方式是廣告-hoc,並且它不是很清楚該函數和它上面聲明的變量是相互關聯的。它也污染了模塊的命名空間,我不太滿意。

後一種方法在樣板上看起來冗長而有點沉重。

我能想到的唯一方法就是將所有這些功能都分解成單獨的文件(模塊)並只導入函數,這樣一切都很乾淨。

有關如何處理此問題的更多經驗豐富的Pythoners的任何建議?或者你只是不擔心它,繼續解決問題?

+1

我個人認爲你的班級創作沒有問題。詳細程度很好。 「'顯式比隱式更好。」 – tMC 2012-08-08 14:18:50

+0

一種至少使用該類的方法不會太冗長,那就是將'try_match'重命名爲'__call__',它可以讓你像構造函數一樣使用它(構造之後)。但是,正如在@ glglgl的回答中,您的JavaScript代碼實際上直接轉換爲Python。 – lvc 2012-08-08 14:27:03

+0

是否在第一個例子中調用someFunc?還是應該是函數定義? – soulcheck 2012-08-08 14:46:50

回答

10

您還可以使用默認參數實現這一點:

def try_match(m, re_match=re.compile(r'sldkjlsdjf').match): 
    return re_match(m) 

,因爲默認參數只計算一次,在模塊導入時間。

或者更簡單:

try_match = lambda m, re_match=re.compile(r'sldkjlsdjf').match: re_match(m) 

或者簡單但卻:

try_match = re.compile(r'sldkjlsdjf').match 

這不僅節省了重新編譯的時候(這實際上是在re模塊中內部緩存反正),但也查找'.match'方法。在繁忙的功能或緊密的循環中,那些'。'決議可以加起來。

+0

感謝您的回答。我知道模塊緩存,但即使在腳本中,我在某些緊密循環中使用單個正則表達式,但通過使用「re.compile」而不是依賴內置緩存,性能顯着提高。 – Cera 2012-08-13 13:37:43

5

什麼

def create_matcher(re): 
    compiled_regex = compile_my_regex() 
    def try_match(m): 
     return compiled_regex.match(m) 
    return try_match 

matcher = create_matcher(r'(.*)-(.*)') 
print matcher("1-2") 

但是在大多數情況下,類更好,更乾淨。

+1

+1」但在大多數情況下,班級更好,更乾淨。「 – tMC 2012-08-08 14:19:45

12

您可以使用在JavaScript中定義閉包的相同方式在Python中定義閉包。

def get_matcher(): 
    compiled_regex = compile_my_regex() 

    def try_match(m) 
     return compiled_regex.match(m) 

    return try_match 

然而,在Python 2.x的倒閉是隻讀(你不能重新分配給compiled_regex函數調用中,對於上面的例子)。如果閉包變量是可變數據結構(例如list,dict,set),則可以在函數調用中對其進行修改。

def get_matcher(): 
    compiled_regex = compile_my_regex() 
    match_cache = {} 

    def try_match(m): 
     if m not in match_cache: 
      match_cache[m] = compiled_regex.match(m) 

     return match_cache[m] 

    return try_match 

在Python 3.x中,你可以使用nonlocal關鍵字在函數調用重新分配給封閉變量。 (PEP-3104)

也可參閱關閉下列問題在Python:

+1

只需添加:Python 3引入了'nonlocal',它可以用來給封閉的變量提供顯式的寫入權限。 [(PEP 3104)](http://www.python.org/dev/peps/pep-3104/) – rwos 2012-08-08 14:30:43

+1

更新了答案 – Imran 2012-08-08 14:33:49

+0

我對這個有點困惑。爲什麼每次運行'get_matcher()'都不會被評估?或者是爲了做一些像'try_match = get_matcher()'這樣的目的? – Cera 2012-08-13 13:36:49

1

一個經常使用的慣例是先於私有模塊級全局以下劃線指示它們不是模塊的輸出API的一部分:

# mymodule.py 

_MATCHER = compile_my_regex() 

def try_match(m): 
    return _MATCHER.match(m) 

您不應該因此而氣餒 - 在函數閉包中最好隱藏一個變量。

2

您可以在任何函數中存儲屬性。由於函數名稱是全局的,因此您可以在其他函數中檢索它。例如:

def memorize(t): 
    memorize.value = t 

def get(): 
    return memorize.value 

memorize(5) 
print get() 

輸出:

5 

你可以用它來存儲在一個單一的功能狀態:

def memory(t = None): 
    if t: 
     memory.value = t 
    return memory.value 

print memory(5) 
print memory() 
print memory() 
print memory(7) 
print memory() 
print memory() 

輸出:

5 
5 
5 
7 
7 
7 

授予它的用處是有限的。我只在this question上使用過它。

相關問題