2011-05-29 125 views
8

我寫了這個代碼:蟒蛇noobie範圍界定問題

x = 0 
def counter(): 
x = 1 
def temp(self): 
    print x 
    x += 1 
return temp 

試圖測試,如果蟒蛇是詞彙或動態範圍。我的想法是,

y = counter() 
y() 

要麼打印0或1,這將告訴我如何python作用域。但是,調用y會拋出異常,表示x未定義。我對Python的工作原理似乎有些根本性的缺陷。

有人可以解釋這是如何工作的?是的,我知道這可以很容易地使用對象完成。我正試圖探索在不使用對象的情況下賦予函數狀態的想法。我以這種方式編寫代碼,因爲上面翻譯成像Scheme這樣的詞彙範圍的語言肯定會起作用。

+0

可能重複http://stackoverflow.com/questions/2009402/read -write-python-closures) – 2011-05-29 23:14:10

回答

2

Python中的閉包不可寫,所以你不能用這種方式編寫代碼。如果你只從函數內部的變量讀取,你應該是好的。在Python 3中,您可以使用關鍵字nonlocal來獲得所需的行爲。

如果您使用Python 2.5或更高版本,還可以使用yield關鍵字將上面的代碼編寫爲生成器。

10

the docs

的Python的一個特別之處就是 - 如果沒有 全球聲明生效 - 分配到的名字總是進入 最裏面的範圍。作業 不復制數據 - 它們僅將名稱 綁定到對象。

所以,當Python解析

def temp(self): 
    print x 
    x += 1 

它看到分配x += 1,因此決定x必須在最裏面的範圍。稍後,您通過y()調用temp(...) - (順便提一句,self應該從temp的定義中省略,否則y()應該提供一個參數) - Python遇到print x語句並且發現x尚未定義在本地(最內層)範圍內。因此錯誤,

UnboundLocalError: local variable 'x' referenced before assignment 

如果聲明

def temp(self): 
    global x 

那麼Python會尋找x在全球範圍內(其中x=0)。 在Python2中,沒有辦法告訴Python在擴展範圍內尋找x(其中x=1)。但在Python3可以與聲明中實現

def temp(self): 
    nonlocal x 
6

Python有兩個作用域(這實際上是三個),加上特殊的規則嵌套本地範圍。這兩個示波器是global(對於模塊級別的名稱),以及本地(對於函數中的任何內容)。除非在函數中聲明瞭global聲明,否則您在函數中分配的任何東西都將自動爲本地聲明。如果您使用的名稱未分配給函數中的任何位置,則它不是本地名稱; Python將(在詞彙上)搜索嵌套函數,以查看它們是否具有該名稱作爲本地名稱。如果沒有嵌套函數或者名稱不在其中的任何位置,則名稱被假定爲全局的。

(全局命名空間是特別的好,因爲它實際上是模塊全局命名空間和內置命名空間,這是藏在builtins__builtins__模塊中的兩個。)

在你的情況,你有三個x變量:一個在模塊(全局)範圍內,一個在counter函數中,另一個在temp函數中 - 因爲+=也是一個賦值語句。由於分配給名稱這一簡單事實使其對函數本地化,因此您的+=語句將嘗試使用尚未分配的局部變量,並且會引發UnboundLocalError。

如果用於所有這三個x引用來引用全局變量的,你需要做的global x同時在countertemp功能。在Python 3.x(但不是2.x)中,有一個nonlocal聲明,就像global一樣,您可以使用temptemp分配給counter中的變量,但保留單獨的全局x

2

Python文檔回答具體問題:http://docs.python.org/reference/executionmodel.html#naming-and-binding

總之,Python有靜態作用域規則。如果函數f定義或刪除了一個變量名,那麼變量名引用函數f的閉包中的一個變量。如果函數f僅使用變量名稱(不定義或刪除),則該名稱引用f的父範圍中的名稱。繼續前往父級範圍,直至找到定義或達到全局範圍。例如:

def f1(x): 
    def g(y): 
    z.foo = y # assigns global z 
    g(1) 

def f2(x): 
    def g(y): 
    x.foo = y # assigns f2's variable, because f2 defines x 
    g(1) 

def f3(x): 
    def g(y): 
    x = C() 
    x.foo = y # assigns g's variable, because g defines x 
    g(1) 

global關鍵字和(在Python 3)nonlocal關鍵字覆蓋默認作用域規則。

儘管變量名稱是靜態解析的,但變量是動態解析的。變量的值來自最近一次定義或在變量被訪問時刪除該變量。值在變量所在的函數閉包中查找。

+0

無論Python人員怎麼稱呼它,你忘記在Python函數中初始化的變量都有動態範圍。這是Python更尖銳的瑕疵之一(不一定它是動態範圍,但它有時是*動態範圍)。 – 2013-02-26 19:19:12

+0

@ChrisPacejo:什麼意思是「動態範圍」(而不是詞法範圍)? – user1071847 2017-06-20 13:13:17

1

感謝您的回覆。以防萬一有人在意,我想出了一個解決這個問題的方法。我通過創建一個「scoper」函數來建立一個計數器變量。

>>> def gc(): 
... def scoper(): 
... scoper.s = 0 
... def rtn(): 
... scoper.s += 1 
... return scoper.s 
... return rtn 
... return scoper() 

上述允許我這樣做,它模仿正確的關閉:

>>> a = gc() 
>>> a() 
1 
>>> a() 
2 
>>> a() 
3 
>>> b = gc() 
>>> b() 
1 
>>> a() 
4 
>>> b() 
2 
[讀/寫的Python閉包(指