2010-07-05 43 views
13

我可以定義一個對象,併爲其分配的屬性和方法:物品或封閉物 - 何時使用?

class object: 
    def __init__(self,a,b): 
     self.a = a 
     self.b = b 
    def add(self): 
     self.sum = self.a + self.b 
    def subtr(self): 
     self.fin = self.sum - self.b 
    def getpar(self): 
     return self.fin 

obj = object(2,3) 
obj.add() 
obj.subtr() 
obj.getpar() 

或通過定義一個閉合提供相同的功能:

def closure(a,b): 
    par = {} 
    def add(): 
     par.update({'sum':a+b}) 
    def subtr(): 
     par.update({'fin':par['sum']-b}) 
    def getpar(): 
     return par['fin'] 
    return {'add':add,'subtr':subtr,'getpar':getpar} 

clos = closure(2,3) 
clos['add']() 
clos['subtr']() 
clos['getpar']() 

我覺得對象語法看起來吸塵器大多數觀衆,但有沒有使用閉包在語義上更可取的實例?

+1

另請參閱http://stackoverflow.com/questions/256625/when-to-use-closure/256651#256651。 ;) – FMc 2010-07-05 23:49:29

+1

:)是的斯蒂芬應該在Qc Na下學習。 – Yuji 2010-07-06 00:05:44

+0

他還在學徒嗎? :) – hatmatrix 2010-07-07 02:11:36

回答

7

在Python中,閉包可能比較常見的對象更難以調試和使用(您必須將可調用的對象保存在某處,使用愚蠢的符號clos['add']等來訪問它們)。想想看,例如,如果你發現結果中有些奇怪的東西,就不可能訪問sum ......調試這種東西可能非常困難;-)

唯一真正的補償優勢是簡單 - 但它基本上只適用於到真正簡單的案例(當你有三個內部函數的可調參數時,我會說你在這方面太過分了)。

也許很強的保護(VS對象的保護‘按照慣例’類和對象實例),或者可能更高熟悉到JavaScript程序員,可能證明使用閉包,而不是類和實例在邊緣的情況下,但在實踐中,我並沒有發現自己在這種假設優勢似乎實際應用的情況下 - 所以我只在非常簡單的情況下使用閉包;-)。

+0

真正的更好的調試類支持...... – hatmatrix 2010-07-07 02:09:14

+0

如果您使用函數屬性而不是字典,封閉具有與類相同的用法。我會在下面寫一個答案。 – JDG 2017-12-12 09:01:29

1

該類的調用語法看起來更好,你可以繼承類。關閉還涉及重複您希望在return聲明中公開的所有名稱。也許這是好的,但如果你想擁有比通常的下劃線前綴約定更隱蔽的真正的私有方法

+0

是的,我不經常子類,但這是一個對象的強有力的論據 – hatmatrix 2010-07-07 02:09:43

+0

我很好奇你的意見是關於我的評論下面,其中函數屬性用於創建一個對象將獲得相同的語法加上增加的便利除非明確地將它包含在return語句中,否則不會(很容易)暴露函數內部函數。 – JDG 2017-12-12 09:13:42

15

你應該使用最清楚地表達你想要實現的內容的版本。

在給出的例子中,我會說對象版本更加清晰,因爲它似乎是對狀態發生變化的對象進行建模。查看使用該值的代碼,對象版本似乎表達了明確的意圖,而封閉版本似乎有操作(索引和「魔術」字符串),這些操作並不重要。

在Python中,當需要的東西大多是一個函數,並且可能需要捕獲一些狀態時,我會傾向於基於閉包的方法。

def tag_closure(singular, plural): 
    def tag_it(n): 
     if n == 1: 
      return "1 " + singular 
     else: 
      return str(n) + " " + plural 
    return tag_it 

t_apple = tag_closure("apple", "apples") 
t_cherry = tag_closure("cherry", "cherries"); 
print t_apple(1), "and", t_cherry(15) 

這也許是比下面更清楚一點:

class tag_object(object): 
    def __init__(self, singular, plural): 
     self.singular = singular 
     self.plural = plural 

    def tag(self, n): 
     if n == 1: 
      return "1 " + self.singular 
     else: 
      return str(n) + " " + self.plural 

t_apple = tag_object("apple", "apples") 
t_cherry = tag_object("cherry", "cherries"); 
print t_apple.tag(1), "and", t_cherry.tag(15) 

作爲一個經驗法則:如果事情是真的只有一個單一的功能,並且僅捕獲靜止狀態,然後再考慮封閉。如果事物打算具有可變狀態,並且/或者具有多個功能,則使用一個類。

另一種說法:如果你正在創建一個封閉字典,你實質上是手工複製了這個類的機器。最好將其留給專門設計的語言結構。

+2

+1強調代碼的可讀性/清晰性作爲決策點。 – spade78 2010-12-15 03:24:29

3

我可以看到使用閉包的唯一真正原因是,如果你想要一個相當有力的保證你的對象/閉包的用戶不能訪問隱藏變量。

事情是這樣的:

class Shotgun: 
    def __init__(self): 
     me = {} 
     me['ammo'] = 2 
     def shoot(): 
     if me['ammo']: 
      me['ammo'] -= 1 
      print "BANG" 
     else: 
      print "Click ..." 
     self.shoot = shoot 

s = Shotgun() 
s.shoot() 
s.shoot() 
s.shoot() 
+3

我不確定我們可以依靠閉包作爲使變量變爲私有的一種方式。可以用's.shoot.func_closure [0] .cell_contents ['ammo'] = 1e20' – unutbu 2010-07-06 12:10:03

+0

對's = Shotgun()'進行翻新。你是對的。依靠這種安全性並不是一個好主意。 – phoku 2010-07-06 12:25:32

+2

深入挖掘關閉聽起來不如直接使用'obj.variable ='符號至少訪問類對象的內部。 – spade78 2010-12-15 03:07:50

1

我提出的方法,使用功能屬性,閉包可以利用相同的語法類,因爲功能在Python對象的影響的評論。所有內部變量也可以像類方法一樣訪問。

我很好奇,如果這種方法做壞事,我不知道。

def closure(a, b): 
    def add(): 
     closure.sum = a + b 
    def subtr(): 
     closure.fin = closure.sum - b 
    def getpar(): 
     return closure.fin 
    closure.add = add; 
    closure.subtr = subtr 
    closure.getpar = getpar 
    return closure 

clo = closure(2,3) 
clo.add() 
clo.subtr() 
print(clo.getpar()) 
print(clo.sum) 
print(clo.fin)