作用域
變量到底是什么呢?可將其視為指向值的名稱。因此,執行賦值語句x = 1
后,名稱x
指向值1
。這幾乎與使用字典時一樣(字典中的鍵指向值),只是你使用的是“看不見”的字典。實際上,這種解釋已經離真相不遠。有一個名為vars
的內置函數,它返回這個不可見的字典:
>>> x = 1 >>> scope = vars() >>> scope['x'] 1 >>> scope['x'] += 1 >>> x 2
警告 一般而言,不應修改
vars
返回的字典,因為根據Python官方文檔的說法,這樣做的結果是不確定的。換而言之,可能得不到你想要的結果。
這種“看不見的字典”稱為命名空間或作用域。那么有多少個命名空間呢?除全局作用域外,每個函數調用都將創建一個。
>>> def foo(): x = 42 ... >>> x = 1 >>> foo() >>> x 1
在這里,函數foo
修改(重新關聯)了變量x
,但當你最終查看時,它根本沒變。這是因為調用foo
時創建了一個新的命名空間,供foo
中的代碼塊使用。賦值語句x = 42是在這個內部作用域(局部命名空間)中執行的,不影響外部(全局)作用域內的x
。在函數內使用的變量稱為局部變量(與之相對的是全局變量)。參數類似於局部變量,因此參數與全局變量同名不會有任何問題。
>>> def output(x): print(x) ... >>> x = 1 >>> y = 2 >>> output(y) 2
到目前為止一切順利。但如果要在函數中訪問全局變量呢?如果只是想讀取這種變量的值(不重新關聯它),通常不會有任何問題。
>>> def combine(parameter): print(parameter + external) ... >>> external = 'berry' >>> combine('Shrub') Shrubberry
警告 像這樣訪問全局變量是眾多bug的根源。務必慎用全局變量。
“遮蓋”的問題
讀取全局變量的值通常不會有問題,但還是存在出現問題的可能性。如果有一個局部變量或參數與你要訪問的全局變量同名,就無法直接訪問全局變量,因為它被局部變量遮住了。
如果需要,可使用函數globals
來訪問全局變量。這個函數類似於vars
,返回一個包含全局變量的字典。(locals
返回一個包含局部變量的字典。)
例如,在前面的示例中,如果有一個名為parameter
的全局變量,就無法在函數combine
中訪問它,因為有一個與之同名的參數。然而,必要時可使用globals()['parameter']
來訪問它。
>>> def combine(parameter): ... print(parameter + globals()['parameter']) ... >>> parameter = 'berry' >>> combine('Shrub') Shrubberry
重新關聯全局變量(使其指向新值)是另一碼事。在函數內部給變量賦值時,該變量默認為局部變量,除非你明確地告訴Python它是全局變量。那么如何將這一點告知Python呢?
>>> x = 1 >>> def change_global(): ... global x ... x = x + 1 ... >>> change_global() >>> x 2
作用域嵌套
Python函數可以嵌套,即可將一個函數放在另一個函數內,如下所示:
def foo(): def bar(): print("Hello, world!") bar()
嵌套通常用處不大,但有一個很突出的用途:使用一個函數來創建另一個函數。這意味着可像下面這樣編寫函數:
def multiplier(factor): def multiplyByFactor(number): return number * factor return multiplyByFactor
在這里,一個函數位於另一個函數中,且外面的函數返回里面的函數。也就是返回一個函數,而不是調用它。重要的是,返回的函數能夠訪問其定義所在的作用域。換而言之,它攜帶着自己所在的環境(和相關的局部變量)!
每當外部函數被調用時,都將重新定義內部的函數,而變量factor
的值也可能不同。由於Python的嵌套作用域,可在內部函數中訪問這個來自外部局部作用域(multiplier
)的變量,如下所示:
作用域嵌套函數的調用方式:
def multiplier(factor): def multiplyByFactor(number): return number * factor return multiplyByFactor >>> double = multiplier(2) >>> double(5) 10 >>> triple = multiplier(3) >>> triple(3) 9 >>> multiplier(5)(4) 20
像multiplyByFactor
這樣存儲其所在作用域的函數稱為閉包。
通常,不能給外部作用域內的變量賦值,但如果一定要這樣做,可使用關鍵字nonlocal
。這個關鍵字的用法與global
很像,讓你能夠給外部作用域(非全局作用域)內的變量賦值。
global和nonlocal區別:
第一,兩者的功能不同。global關鍵字修飾變量后標識該變量是全局變量,對該變量進行修改就是修改全局變量,而nonlocal關鍵字修飾變量后標識該變量是上一級函數中的局部變量,如果上一級函數中不存在該局部變量,nonlocal位置會發生錯誤(最上層的函數使用nonlocal修飾變量必定會報錯)。
第二,兩者使用的范圍不同。global關鍵字可以用在任何地方,包括最上層函數中和嵌套函數中,即使之前未定義該變量,global修飾后也可以直接使用,而nonlocal關鍵字只能用於嵌套函數中,並且外層函數中定義了相應的局部變量,否則會發生錯誤(見第一)。
詳細說明地址:https://blog.csdn.net/xcyansun/article/details/79672634
函數id()的用法:
功能:返回變量地址
也就是is對比為真的根源,地址相同則相同為真,而相等並不一定相同。