2012-12-26 36 views
7

我最近着迷於Python。以前,我主要使用C++和Matlab編程數值和數據分析代碼。我看到很多關於Python,Ruby和閉包的討論。幾乎所有的例子都是這樣的:關閉:什麼是一個好的用例示例?爲什麼不是一個函子?這是值得的否定嗎?

>>> def makeAdder(y): 
... def myAdder(x): 
... return x + y 
... return myAdder 
... 
>>> f = makeAdder(10) 
>>> f(5) 
15 

我知道這在某種意義上是有用的。然而,現實的是,在這樣的情況下的行爲(「只讀」的情況下,因爲它是)可以容易地通過一個對象(算符)進行仿真:

>>> class MyAdder(object): 
... def __init__(self,y): 
... self.y = y 
... def __call__(self,x): 
... return self.y + x 
... 
>>> f = MyAdder(5) 
>>> f(10) 
15 

對象不佔用基本上更多的空間來代碼,而且它更加通用。跟蹤和調試後續代碼也容易得多。

在這種情況下,我們只從非局部變量讀取。但是我們也可以編寫它:在Ruby中,當然,在Python中使用nonlocal關鍵字。該對象當然也支持這一點。但是,通過這個對象,您可以將數據捆綁在一起,以便您確切知道發生了什麼。閉包可能會以完全不透明的方式攜帶變量,這可能導致代碼難以調試。這是一個非常奇怪的例子:

irb(main):001:0> def new_counter 
irb(main):002:1> x = 0 
irb(main):003:1> lambda { x +=1 } 
irb(main):004:1> end 
=> nil 
irb(main):005:0> counter_a = new_counter 
=> #<Proc:[email protected](irb):3> 
irb(main):006:0> counter_a.call 
=> 1 
irb(main):007:0> counter_a.call 
=> 2 

至少對我而言,這種行爲是不直觀的。它也有可能導致內存泄漏。這給你一個巨大的繩子掛上自己。同樣,在Ruby中,你不需要明確地啓用它(與Python不同),而且在Ruby中,所有的主代碼都可以訪問所有的代碼。如果外部變量由於處於閉包而發生變化,如果你傳遞閉包,你可以有一個變量無限期地遠離它所在的地方。與始終安全地攜帶其數據的對象相反。

爲什麼你會聽到很多關於閉包有多好的說法,以及它們應該如何包含在Java中,當它們不完全在Python中時它是如何被吸引的?爲什麼不使用函子?或者重構代碼以避免它們的危險程度如何?只是爲了澄清,我不是那些在OO類型中起泡的人之一。我是低估了他們的使用,誇大了他們的危險,還是兩者兼而有之?

編輯:也許我應該區分三件事情:只讀一次(這是我的例子顯示,幾乎每個人都討論)的閉包,一般讀取的閉包和寫入的閉包。如果使用外部函數的局部變量在另一個函數內部定義一個函數,那麼幾乎沒有機會回到你身邊。該空間中的變量無法以我能想到的方式訪問,因此您無法更改它。這是非常安全的,並且是一種方便的(可能比函子更多的)生成函數的方式。另一方面,如果您在類方法內部或在主線程內部創建閉包,則每次調用變量時都會讀取可從其他位置訪問的變量。所以它可以改變。我認爲這很危險,因爲封閉的over變量沒有出現在函數頭部。你可以在你的代碼的第1頁上說一個關閉主線程變量x的長關閉,然後爲了不相關的原因修改x。然後重新使用閉包,並得到你不明白的怪異行爲,這可能很難調試。 如果你真的寫封閉變量,那麼就像我用Ruby的例子表明你真的有可能弄得一團糟,並導致意外的行爲。

Edit2:我給出了第三個用法的閉包中的奇怪行爲的例子,寫入非局部變量。下面是從第二利用離奇的(不是那麼糟糕)行爲的一個例子(限定範圍倒閉,他們的封閉過的變量可以被修改):

>>> fs = [(lambda n: i + n) for i in range(10)] 
>>> fs[4](5) 
14 
+0

我不知道紅寶石,所以我不知道,如果你的內存要求泄漏是現實的。這不是Python中的問題,也許你可以澄清這一點? –

+0

同意,特別是我不知道哪裏*非常危險*來自。 –

+1

我認爲它主要歸結爲Python不是C++,Ruby不是C++,JavaScript不是C++,Perl不是C++,...如果你嘗試在任何地方使用函子,那麼人們會想知道爲什麼你要寫Python中的C++。我可能會說,函子是沒有GC和閉包的語言的混雜或實現細節,更多動態語言(通常)不需要的混合語言。而當你不需要所有這些機器時,爲什麼要定義一個全新的課程? –

回答

10

可讀性。你的Python例子展示了將閉包版本與函數進行比較的更明顯和容易的地方。

我們還整齊地避免做一個什麼都不做的類,而是像一個函數一樣 - 這種冗餘的味道。

如果沒什麼別的,當我們是東西,it makes sense to describe it as an action, not an object

作爲另一個說明,在Python中使用這些結構的lot的一個示例非常有效,它是裝飾器。大多數函數裝飾器都是這樣做的,它們是一個非常有用的功能。

編輯:作爲情況的說明,請記住,功能是不是在Python特殊,他們仍然反對:

>>> def makeAdder(y): 
...  def myAdder(x): 
...   return x + myAdder.y 
...  myAdder.y = y 
...  return myAdder 
... 
>>> f = makeAdder(10) 
>>> f(5) 
15 
>>> f.y 
10 
+1

我認爲這是一個品味問題。我個人同意,但是我遇到過很多人,他們發現函數返回函數的想法很難理解,因爲當外部函數和內部函數被調用時,他們無法得到他們的頭。 – BrenBarn

+3

@BrenBarn鑑於Python的動態性質,函數是一階對象的想法是一個非常重要的概念,而且我沒有看到類似函數的類更容易理解。並不是說我沒有看到你的觀點,但我認爲這不是真的對他們有害。 –

+1

我想人們可以問,它是否真的更具可讀性。畢竟:這不僅僅是一個功能,它也具有狀態。此外,如果您以後需要檢查有關狀態的任何內容,則使用對象更容易。 –

相關問題