深入瞭解python全局變量,局部變量和命名空間

Python 使用全局和局部變量的方式是特立獨行的。雖然在許多或大多數其他編程語言中,如果未另行聲明,變量將被視為全局變量,而 Python 則以相反的方式處理變量。如果沒有另外聲明,它們是本地的。這種方法背後的驅動原因是全局變量通常是不好的做法,應該避免。在大多數情況下,您想使用全局變量,最好使用參數將值放入函數或返回值以將其取出。與許多其他程序結構一樣,Python 也通過設計強加瞭良好的編程習慣。

因此,當您在函數定義中定義變量時,默認情況下它們是該函數的局部變量。也就是說,您對函數體中的此類變量所做的任何操作都不會影響函數外的其他變量,即使它們具有相同的名稱。換句話說,函數體是這樣一個變量的范圍,即這個名稱與其值相關聯的封閉上下文。

所有變量都有塊的作用域,它們在那裡被聲明和定義。它們隻能在聲明點之後使用。

簡單說一下:變量不必也不能以在 Java 或 C 等編程語言中聲明的方式聲明。Python 中的變量通過定義它們來隱式聲明,即第一次分配值到一個變量,這個變量被聲明並自動具有必須分配給它的對象的數據類型。如果您在理解這一點時遇到問題,請參閱我們關於數據類型和變量的章節,請參閱左側的鏈接。

函數中的全局和局部變量

在下面的示例中,我們想演示如何在函數體內使用全局值:

def  f ():  
    print ( s )  
s  =  “我愛夏天的巴黎!” 
f ()

輸出:

我愛夏天的巴黎!

在調用函數 f() 之前,變量 s 被定義為字符串“我愛夏天的巴黎!”。f() 的主體僅由“print(s)”語句組成。由於沒有局部變量 s,即沒有賦值給 s,將使用全局變量 s 的值。所以輸出將是字符串“我愛夏天的巴黎!”。問題是,如果我們在函數 f() 中改變 s 的值會發生什麼?它也會影響全局變量嗎?我們在下面的一段代碼中對此進行瞭測試:

def  f ():  
    s  =  "我愛倫敦!" 
    打印( s ) 
s  =  “我愛巴黎!”  
f ()
打印( s )

輸出:

我愛倫敦!
我愛巴黎!

如果我們將第一個示例與第二個示例結合起來,即我們首先使用 print() 函數訪問 s,希望獲得全局值,然後為其分配一個新值呢?給它賦值,意味著 – 正如我們之前所說的 – 創建一個局部變量 s。因此,我們會將 s 作為同一范圍內的全局變量和局部變量,即函數體。幸運的是,Python 不允許這種歧義。因此,它會引發錯誤,正如我們在以下示例中所見:

def  f ():  
   print ( s ) 
   s  =  “我愛倫敦!” 
   打印( s )
s  =  “我愛巴黎!” 
f ()

輸出:

UnboundLocalError Traceback (最近一次調用最後一次)
<ipython-input-3-d7a23bc83c27> in <module>
5
6 s = “我愛巴黎!”
—-> 7 f ( )
<ipython-input-3-d7a23bc83c27> in f ()
1 def f ( ) :
—-> 2 print ( s )
3 s = “我
愛倫敦!” 4 打印( s )
5
UnboundLocalError:賦值前引用瞭局部變量“s”

變量不能在函數內既是局部的又是全局的。由於在 f() 內部為 s 賦值,因此 Python 決定我們需要一個局部變量,因此在 s 定義之前的第一個打印語句拋出瞭上面的錯誤信息。任何在函數內部更改或創建的變量都是局部變量,如果它沒有被聲明為全局變量。要告訴 Python,我們要使用全局變量,我們必須使用關鍵字“global”明確說明這一點,如下例所示:

def  f (): 
    global  s 
    print ( s ) 
    s  =  "隻在春天,但倫敦也很棒!" 
    打印( s )

s  =  "我在巴黎找課程!"  
f ()
打印( s )

輸出:

我正在巴黎尋找課程!
隻在春天,但倫敦也很棒!
隻在春天,但倫敦也很棒!

函數調用完成後,不能從外部訪問函數的局部變量。這是上一個例子的延續:

def  f (): 
    s  =  "我在全球范圍內不為人知"
    打印( s ) 
f ()
打印( s )

輸出:

我在全球不為人知
隻在春天,但倫敦也很棒!

以下示例顯示瞭局部和全局變量以及函數參數的狂野組合:

def  foo ( x ,  y ):
    全局 a 
    a  =  42 
    x , y  =  y , x 
    b  =  33 
    b  =  17 
    c  =  100
    打印( a , b , x , y )
a ,  b ,  x ,  y  =  1 ,  15 ,  3 , 4  
foo ( 17 ,  4 )
打印( a ,  b ,  x ,  y )

輸出:

42 17 4 17
42 15 3 4

嵌套函數中的全局變量

如果我們在嵌套函數中使用 global 關鍵字,我們現在將檢查會發生什麼。以下示例顯示瞭在各種范圍內使用變量“city”的情況:

def  f (): 
    city  =  "Hamburg" 
    def  g (): 
        global  city 
        city  =  "Geneva" 
    print ( "調用前g:"  +  city ) 
    print ( "現在調用g:" ) 
    g () 
    print ( "調用後g: "  + 城市)
f () 
print ( "主城的值:"  +  city )

輸出:

之前打電話給g:漢堡
現在調用 g:
打電話後g:漢堡
主要城市價值:日內瓦

我們可以看到嵌套函數 g 中的 global 語句不會影響函數 f 的變量“city”,即它保持其值“Hamburg”。我們還可以從這個例子中推斷出,在調用 f() 之後,模塊命名空間中存在一個變量 ‘city’,其值為 ‘Geneva’。這意味著嵌套函數中的 global 關鍵字不會影響其封閉命名空間的命名空間!這與我們在前一章中發現的一致:在函數內部定義的變量是局部變量,除非它明確標記為全局變量。換句話說,我們可以在任何封閉作用域中引用一個變量名,但我們隻能在局部作用域中通過賦值重新綁定變量名,或者通過使用全局聲明在模塊全局作用域中重新綁定變量名。我們還需要一種方法來訪問其他作用域的變量。這樣做的方法是非局部定義,我們將在下一章解釋。

非局部變量

Python3 引入瞭非局部變量作為一種新的變量。非局部變量與全局變量有很多共同點。與全局變量的一個區別在於,無法通過使用非局部語句來更改模塊范圍內的變量,即未在函數內部定義的變量。我們在以下兩個示例中展示瞭這一點:

def  f ():
    全球 城市
    打印( city )
city  =  "法蘭克福" 
f ()

輸出:

法蘭克福

該程序是正確的,並返回“Frankfurt”作為輸出。我們將在以下程序中將“全局”更改為“非本地”:

def  f ():
    非本地 城市
    打印( city )
city  =  "法蘭克福" 
f ()

輸出:

文件“<ipython-input-9-97bb311dfb80>” ,第2
行 非本地城市
^
語法錯誤:未找到非本地“城市”的綁定

這表明非局部綁定隻能在嵌套函數內部使用。必須在封閉的函數作用域中定義非局部變量。如果變量未在封閉函數作用域中定義,則變量不能在嵌套作用域中定義。這是與“全局”語義的另一個區別。

def  f (): 
    city  =  "Munich" 
    def  g (): 
        nonlocal  city 
        city  =  "Zurich" 
    print ( "調用前g:"  +  city ) 
    print ( "現在調用g:" ) 
    g () 
    print ( "調用後g: "  + 城市)
city  =  "Stuttgart" 
f () 
print ( "'city' in main:"  +  city )

輸出:

打電話之前 g: 慕尼黑
現在調用 g:
撥打 g 後:蘇黎世
主要的“城市”:斯圖加特

在前面的例子中,變量 ‘city’ 是在調用 g 之前定義的。如果沒有定義,我們會得到一個錯誤:

高清 ˚F ():
    #city = “慕尼黑”
    高清 g ^ ():
        外地 市
        城市 =  “蘇黎世”
    打印(“呼叫摹前:”  + 城市)
    打印(“立即致電G:” )
    g ^ ()
    打印(“後呼叫 g: "  +  city )
city  =  "Stuttgart" 
f () 
print ( "'city' in main:"  +  city )

輸出:

文件“<ipython-input-11-5417be93b6a6>” ,第4
行 非本地城市
^
語法錯誤:未找到非本地“城市”的綁定

該程序運行良好 – 如果我們將“非本地”更改為“全局”,在 f – 內有或沒有 ‘city = “Munich”‘ 行:

def  f (): 
    #city = "Munich" 
    def  g (): 
        global  city 
        city  =  "Zurich" 
    print ( "Before call g:"  +  city ) 
    print ( "Calling g now:" ) 
    g () 
    print ( "After 調用g:" )呼叫 g: " +  city )
city  =  "Stuttgart" 
f () 
print ( "'city' in main:"  +  city )

輸出:

打電話之前:斯圖加特
現在調用 g:
撥打 g 後:蘇黎世
主要的“城市”:蘇黎世

然而有一個巨大的不同:全局 x 的值現在發生瞭變化!

推薦閱讀: