2012-06-22 98 views
9

我期待這個小片段打印「爲什麼這不工作?」有人可以幫助我理解爲什麼這不起作用,因爲我期望?如果這很重要,我正在使用Python 2.6。Python關閉+全球奇怪

class WhyDoesntThisWork(object): 
    def outer(self): 
    acc = '' 
    def inner(msg): 
     global acc 
     acc = acc + msg 
    inner("Why doesn't") 
    inner(" this work?") 
    print acc 
WhyDoesntThisWork().outer() 
  • 如果我包括global聲明,我收到了NameError: global name 'acc' is not defined
  • 如果我不包括global聲明,我會得到一個UnboundLocalError: local variable 'acc' referenced before assignment
+4

'global'不適用於關閉。你需要python 3中的'nonlocal'。 –

+1

改用mutable(沒有全局關鍵字); 'acc = []','acc.append'等 –

+0

好的,所以使用可變('acc = []')的作品。你能解釋爲什麼那麼重要......只是爲了稍後的瑣事? – sholsapp

回答

8

我不知道爲什麼這麼多評論上面包含正確的答案,沒有人敢寫一個實際的答案,所以我會做到這一點。

class ThisWorksNow(object): 
    def outer(self): 
    acc = [] 
    def inner(msg): 
     acc.append(msg) 
    inner("Why doesn't") 
    inner(" this work?") 
    print "".join(acc) 
ThisWorksNow().outer() 

有什麼區別?

爲封閉對象指定名稱在Python 2.x中不起作用,因爲缺少Py3關鍵字nonlocal,所以我們必須找到解決方法。

如果我們必須保持名稱到對象的綁定常量,我們必須改變別的東西。在這種情況下,它是我們添加要添加的內容的對象。

print行不是很優雅;也許打印連接內容的對象可能更適合。

class StringBuilder(list): # class name stolen from Java 
    def __str__(self): 
     """this makes the object printable in a way which represents the concatenated string""" 
     return "".join(self) 
    @property 
    def string(self): 
     """this gives us a property which represents the concatenated string""" 
     return "".join(self) 
# use whatever suits you better, one or both 

有了這個,我們可以這樣做:

class ThisWorksNow(object): 
    def outer(self): 
    acc = StringBuilder() 
    def inner(msg): 
     acc.append(msg) 
    inner("Why doesn't") 
    inner(" this work?") 
    print acc 
    print acc.string # depending on what you take above 
ThisWorksNow().outer() 

編輯(追加):爲什麼global不行?

我們也可以通過global來實現這一點,其中2個缺點。

  1. acc將不得不作出全球在這兩個地方,我們用它

    class WhyDoesntThisWork(object): 
        def outer(self): 
        global acc 
        acc = '' 
        def inner(msg): 
         global acc 
         acc = acc + msg 
    

    在此,我們「提升」兩acc事件爲「global」的水平。

  2. acc可以從外面修改。

    如果我們在其他地方做global acc,或者我們在模塊級使用acc,我們的過程可能會被篡改。這應該避免。

+0

請澄清。請不要僅僅認爲'nonlocal'關鍵字能夠讓它起作用,請解釋在那個地方使用'global'關鍵字會發生什麼的機制。解釋器等忽略了「全局」關鍵字。 – wberry

+0

我想我明白了。這意味着'global'關鍵字確實只適用於在模塊級定義的變量;而且正如一些評論者所聲稱的那樣,在閉包中使用'global'本身並不是問題。 – wberry

+0

這不是一個大問題,但它是不乾淨的,因爲它會污染模塊級名稱空間並可能導致衝突。 – glglgl

0

您可以像下面那樣創建一個helper閉包類來明確地標記變量爲閉包。不知道這樣的實用程序是否已經存在。

class closure: 
    def __init__(self, val = None): 
     self.val = val 
    def __call__(self, val = None): 
     if val: 
     self.val = val 
     return self.val 
class WhyDoesntThisWork(object): 
    def outer(self): 
    acc = closure('') 
    def inner(msg): 
     acc(acc() + msg) 
    inner("Why doesn't") 
    inner(" this work?") 
    print acc 
WhyDoesntThisWork().outer()