2013-08-16 80 views
4

當我運行這段代碼,我得到這樣的結果:在python中關閉?

15 
15 

我期望輸出應該是

15 
17 

,但事實並非如此。問題是:爲什麼?

def make_adder_and_setter(x): 
    def setter(n): 
     x = n 

    return (lambda y: x + y, setter) 

myadder, mysetter = make_adder_and_setter(5) 
print myadder(10) 
mysetter(7) 
print myadder(10) 
+0

@Wooble:我最終棉花,是的。 :-) –

回答

8

Python 2.x有一個語法限制,不允許在讀/寫中捕獲變量。

的原因是,如果一個變量的函數分配,只有兩種可能:

  1. 變量是一個全球性的,並已被宣佈所以用global x
  2. 變量是本地的功能

更具體地說它排除了該變量是一個局部的封閉功能範圍

這已被Python 3.x替代,並增加了nonlocal聲明。您的代碼將繼續按照預期在Python 3通過改變它

def make_adder_and_setter(x): 
    def setter(n): 
     nonlocal x 
     x = n 

    return (lambda y: x + y, setter) 

蟒蛇2.x的運行時能夠處理讀 - 寫在字節級關閉了變數,但是限制是語法的編譯器接受。

你可以看到一個LISP編譯器直接生成字節碼蟒創建具有讀寫拍攝狀態at the end of this video加法器關閉。編譯器可以爲Python 2.x,Python 3.x或PyPy生成字節碼。

如果您需要封閉在Python中的可變狀態2.x的一招就是用一個列表:

def make_adder_and_setter(x): 
    x = [x] 
    def setter(n): 
     x[0] = n 

    return (lambda y: x[0] + y, setter) 
+0

謝謝,現在我明白了這個問題 –

13

您在setter()功能設置本地變量x。賦值給函數中的名稱會將其標記爲本地名稱,除非您明確地告訴Python編譯器。

在Python 3,你可以使用關鍵字nonlocal明確標示x作爲非本地:

def make_adder_and_setter(x): 
    def setter(n): 
     nonlocal x 
     x = n 

    return (lambda y: x + y, setter) 

現在x被標記爲自由變量,而是在分配給在周邊範圍內擡頭。

在Python 2中,您不能標記爲Python本地。您唯一的其他選項是將x標記爲global。您必須使用技巧來改變位於周圍範圍內的可變對象所包含的值。例如:

屬性setter函數將工作,例如; setter是本地make_adder_and_setter()範圍,該對象的屬性將是任何有訪問setter可見:

def make_adder_and_setter(x): 
    def setter(n): 
     setter.x = n 
    setter.x = x 

    return (lambda y: setter.x + y, setter) 

另一個技巧是使用一個可變的容器,如一個列表:

def make_adder_and_setter(x): 
    x = [x] 
    def setter(n): 
     x[0] = n 

    return (lambda y: x[0] + y, setter) 

在這兩種情況下,你是而不是分配給一個本地名稱了;第一個示例使用setter對象上的屬性分配,第二個示例更改x列表,而不是分配給x本身。

2

你內心def setter(n)函數定義了自己的局部變量x。隱藏其他x變量這是的make_adder_and_setter的參數(使一個孔中的範圍)。所以setter函數沒有副作用。它只是設置一個內部局部變量的值並退出。

也許如果你嘗試下面的代碼,這將是明確的爲您服務。它完全一樣,只是使用名稱z而不是x。

def make_adder_and_setter(x): 
    def setter(n): 
     z = n 

    return (lambda y: x + y, setter) 

myadder, mysetter = make_adder_and_setter(5) 
print myadder(10) 
mysetter(7) 
print myadder(10)