之前跟大家已經講了有關函數的一部分知識,但是忘了講一個很重要的點,就是變數的作用域,這塊知識不只是適用於函數,它試用域所有的Python程式 在正式寫程式之前,必須要清楚這一塊,否則就很容易犯錯誤 首先理清一個概念,什麼是變數 變數可以我們可以將它看為指向值的名稱,就像我們之前講的字典一樣的,只是這 ...
之前跟大家已經講了有關函數的一部分知識,但是忘了講一個很重要的點,就是變數的作用域,這塊知識不只是適用於函數,它試用域所有的Python程式
在正式寫程式之前,必須要清楚這一塊,否則就很容易犯錯誤
首先理清一個概念,什麼是變數
變數可以我們可以將它看為指向值的名稱,就像我們之前講的字典一樣的,只是這個字典你是看不到,當然這是很通俗的解釋,但也離真相不遠了
在Python中有一個vars的內置函數,他可以返回這個看不見的“字典”
比如以下代碼
In [1]: a = 100 In [2]: distrue = vars() In [3]: distrue["a"] Out[3]: 100 In [4]: distrue["a"] += 100 In [5]: a Out[5]: 200
是不是非常神奇,把變數變成了一個鍵值對應的字典,而且還可以通過索引更改值
在這裡要註意一點,根據官方文檔,是不應該修改vars返回的字典的,這樣會使得結果不確定
我們把這種看不見的字典叫做命名空間或者是作用域
作用域
我們現在給出以下代碼
In [7]: a = 100 In [8]: def func_one(): ...: a = 200 ...: In [9]: func_one() In [10]: a Out[10]: 100
一開始,我們定義了一個變數,將100賦值給了a,然後我們後面定義了一個方法,將200賦值給a
之後,我們調用了這個方法,但是,最後a的值並沒有改變,這個就牽涉到有關變數作用域的問題了,我們慢慢解釋
首先我們調用func_one時是創建了一個新的命名空間,以供func_one中的代碼塊使用,而賦值語句a = 200是在這個函數內部的作用域執行的,也就是一個局部命名空間,他是不會影響外部作用域(或者是全局)的變數,
在函數內部使用的變數我們把它成稱為局部變數,在函數外的就稱作全局變數
參數是類似於局部變數的,所以他的命名即使與全局變數相同也是沒有關係的
比如下麵的一個輸出函數
In [12]: def output(x): ...: print(x) ...: In [13]: x = 10 In [14]: y = 20 In [15]: output(y) 20 In [16]: output(x) 10
函數訪問全局變數
只是讀取值得話,非常簡單
比如以下代碼
In [18]: def linkval(words): ...: print(words + external) ...: In [19]: external = " susmote" In [20]: linkval("hello") hello susmote
但是,在這提醒以下,這種方法還是慎用,很容易造成bug
這個時候,我們又遇到了一個新的問題,如果局部變數和全局變數相同的話,就會出現遮蓋的問題
稍微改一下上面的代碼,問題就出現了
In [21]: def linkval(words): ...: external = " jack ma" ...: print(words + external) ...: In [22]: external = " susmote" In [23]: linkval("hello") hello jack ma
也就是說,全局變數被局部變數給遮住了
如果實在有需要的話,可以使用globals函數來訪問全局變數,他會返回一個包含全局變數的字典,也就是說可以通過字典的鍵索引獲取值
(相對的locals返回一個包含局部變數的值)
還是上面的代碼,我們稍稍做一點改變,結果就會截然不同
In [24]: def linkval(words): ...: external = " jack ma" ...: print(words + globals()['external']) ...: In [25]: external = " susmote" In [26]: linkval("hello") hello susmote
函數中定義全局變數
一般我們在函數中定義變數,它預設是局部變數,但是我們可以直接聲明定義的變數為全局變數,這時我們就必須用到global這個關鍵詞了
In [32]: a = 100 In [33]: def func_one(): ...: global a ...: a += 100 ...: In [34]: func_one() In [35]: a Out[35]: 200 In [36]: func_one() In [37]: a Out[37]: 300
作用域的嵌套
之前有一個概念沒有講到,就是函數的定義是可以嵌套的,類似於一個函數中,還可以定義函數,類似於以下代碼
In [45]: def func_one(): ...: def func_two(): ...: print("內層函數") ...: print("外層函數") ...: func_two() ...: In [46]: func_one() 外層函數 內層函數
嵌套函數實際作用並不大,但他的一個突出的功能還是不錯的,使用一個函數來創建另一個函數
例如以下代碼
In [48]: def adder(factor): ...: def addByFactor(number): ...: return number + factor ...: return addByFactor ...: ...: In [49]: adder(10)(10) Out[49]: 20 In [50]: adder(100)(100) Out[50]: 200
一個函數位於另一個函數中,而且外面的函數返回的是裡面的函數,沒有調用這個函數
其中,最重要的一點是,返回的函數能夠訪問其定義所在的作用域,也就是說,他返回時,還帶著他自己所在的環境(相關的局部變數)
放到實際中來說就是可以在內部函數中訪問這個來自外部作用域的局部變數
我們把這種函數稱之為 閉包
如果要給外部作用域的變數賦值,可以用nonlocal關鍵詞,這有點類似於global
關於作用域我要講的就是這些,通過簡單的練習,就能很快理解