2014-01-13 39 views
22

問題是在這篇文章的末尾。如何打印語句創建一個局部變量

第一個片段:空局部變量字典。

def outer(): 
    x = 1 
    def inner(): 
     print "Local variables: %s" % locals() 
    return inner() 
print outer() 

輸出: 局部變量:{}

第二片段:打印內()函數內部和創建本地變量條目。

def outer(): 
    x = 1 
    def inner(): 
     print x 
     print "Local variables: %s" % locals() 
    return inner() 
print outer() 

輸出:

1 
Local variables: {'x': 1} 

第三片段

def outer(): 
    x = 1 
    def inner(): 
     print x 
     print "Local variables: %s" % locals() 
     del x 
    return inner() 
print outer() 

輸出::

>>> outer() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 7, in outer 
    File "<stdin>", line 4, in inner 
UnboundLocalError: local variable 'x' referenced before assignment 
>>> 

問題1從內函數內部德爾X ons:

  1. 在第二個片段中,print語句如何創建局部變量。
  2. 如果它在內部函數中創建局部變量,爲什麼我不能刪除它。

有人可以幫我理解這一點。

+1

快速搜索揭示了一個重複的問題,但它的答案不是很好。答案中的一些信息完全錯誤。我沒有投票結束。 – user2357112

+4

從[Python文檔](http://docs.python.org/2/library/functions.html#locals):「自由變量由['locals()']返回(http://docs.python .org/2/library/functions.html#locals)在功能塊中調用時,不在類塊中調用。「 – user2357112

+0

可能的重複[Python簡介範圍規則](http://stackoverflow.com/questions/291978/short-description-of-python-scoping-rules) –

回答

23

在Python中,除非另行指定(用global聲明,或在一個3.0+聲明nonlocal),一個變量是locals,如果你在函數修改它(分配給它,它del等)在任何地方。*

在第一個片段中,你永遠不會修改x,甚至不訪問它,所以它不是本地的。事實上,它甚至不存在。這很容易。

第二個版本是棘手的。 x對於inner不是本地的,因爲您不在inner中對其進行修改。所以,Python一直在尋找它,通過範圍向外移動範圍,直到它發現它具有該變量的範圍。它在outer中發現它是一個局部變量。這意味着它是一個閉合變量自由變量inner。由於locals函數包含閉包變量以及局部變量,因此您可以看到它。

第三個版本,做del x,使x本地到inner。**所以,它出現在locals。但是,您嘗試print它沒有分配過任何東西,所以沒有任何價值。所以你得到一個UnboundLocalError

一般來說,一旦您瞭解了Python在此嘗試完成的基本概念,通常很明顯您擁有哪種類型的變量。但如果不清楚,詳細規則在Naming and Binding中定義。


如果您想了解如何在封面下工作,可以從檢查函數對象開始。試試這個:

def outer(): 
    x = 1 
    def inner(): 
     print x 
     print "Local variables: %s" % locals() 
    return inner 
inner = outer() 
print inner.func_closure 
print inner.func_code.co_freevars 
print outer.func_code.co_cellvars 

inspect模塊文檔列表中的所有的functioncode的重要成員,和其他「在幕後」的對象。

使用dis模塊來看看字節碼outerinner也可能會有所幫助。***例如,如果運行this code,你會看到,小區A LOAD_FAST本地,LOAD_DEREFLOAD_GLOBAL爲全球。但是如果你真的想了解所有這些真的如何工作,Eli Bendersky的「Python內部」博客文章symbol tables的一系列文章幾乎涵蓋了所有內容。 (感謝阿什維尼·喬杜裏定位,並在註釋中指出來。)


*這是在編譯期進行檢查,沒有執行時間,所以試圖與混淆,例如,exec可以成功地混淆兩者Python和你自己。

**請注意,del既是修改又是訪問。這可能令人驚訝,但是您可以看到def foo(): del x將增加UnboundLocalError,因爲del使本地爲x,並且與del非常相似無法找到值。

*** ...假設您使用的是使用CPython風格的字節碼的Python實現,如CPython本身(當然)或PyPy。

+0

即使我沒有打印,當我嘗試刪除時也得到了同樣的錯誤。 –

+0

@yopy:在這種情況下,您在創建它之前試圖刪除一個變量,這是一個不同的錯誤,但不是_that_不同。你可以看到沒有任何這種花哨的東西:'def foo():del x'。 – abarnert

+1

@Cilyan:'inner'是封閉的。他立即調用閉包並返回其值的事實,而不是返回閉包本身,並不意味着它不是閉包。 – abarnert

8

Python通過查看編譯時如何使用變量來支持嵌套作用域。您在函數中分配給的變量(或者在函數中與import綁定)被認爲是本地的,其他所有變量都是非本地的。試圖刪除一個變量也會將其標記爲本地。

在父範圍內搜索非本地名稱,如果未找到則視爲全局。

在第二個示例中,x引用了父範圍中的名稱。你沒有分配給它,所以它是一個嵌套的名字,並且可以在本地命名空間中看到。它不是,實際上是一個本地名字,而是一個自由變量;它的值取自父範圍。

在上例中,您嘗試刪除x,使其成爲本地名稱。在分配任何東西之前嘗試引用它會導致異常。

這全部記錄在Python參考的Execution model文檔中。具體如下:

當在代碼塊中使用名稱時,將使用最接近的 封閉範圍解決該問題。代碼塊可見的所有這些範圍的集合是 ,稱爲塊的環境

如果一個名稱被綁定在一個塊中,它就是該塊的局部變量。 如果名稱在模塊級綁定,它是一個全局變量。 (模塊代碼塊的 變量是本地和全局變量。)如果在代碼塊中使用了變量 ,但在此處未定義,則變量爲,變量爲

以下構建體結合的名稱:函數的形式參數, import語句,類和函數定義(這些結合在定義塊中 類或函數名),並且是 標識符如果在分配中發生的目標, for循環標題, except子句標題的第二個位置或as與 之間的一個語句。 from ... import *形式的import聲明綁定 導入模塊中定義的所有名稱,但以 開頭的下劃線除外。該表格只能用於模塊級別。

發生在del語句中的目標也被認爲綁定爲 (儘管實際的語義是解除綁定名稱)。它 是非法的,以解除封閉範圍引用的名稱; 編譯器會報告一個SyntaxError