Python第六章-函數02-函數的作用域
- 2020 年 4 月 1 日
- 筆記
函數
三、作用域規則
有了函數之後,我們必須要面對一個作用域的問題。
比如:你現在訪問一個變量,那麼 python 解析器是怎麼查找到這個變量,並讀取到這個變量的值的呢? 依靠的就是作用域規則!
3.1 作用域
作用域(scope
)
作用域就是 python 程序的一塊文本區域,在這個區域內,可以直接訪問(Directly accessible)命名空間。
直接訪問的意思就是:當你訪問一個絕對的命名的時候,直接在命名空間中查找
儘管作用域的定義是靜態的,但是作用域的使用(查找變量)卻是動態的。
在代碼執行的任何時間,至少有 3 個嵌套的作用域,這些作用域的命名空間可以直接訪問。
- 內部作用域(局部作用域)。包含了所有的局部命名,在訪問變量的時候,首先在內部作用域中查找。
- 然後是嵌套函數的外層作用域。在這裡搜索非局部,但也是非全局的命名。(在 python 中允許在函數中定義函數的)
- 然後是包含當前模塊的全局作用域。
- 最後搜索的是最外層的創建內置命名的作用域。
作用域搜索規則:LEGB
L:局部的(local)
E:封閉的(Enclosing)
G:全局的(Global)
B:內置的(Built-in)
一、局部命名空間
函數內部的命名空間,在調用函數的時候生成,調用結束時消失。當局部命名空間有效時,它是第一個用於檢查某個名字存在性的命名空間。如果在局部命名空間內找到該名稱,則返回與名字相關聯的對象,反之提示出錯。
3.2作用域在 python 中的具體應用
3.2.1.訪問局部作用域
def foo(): a = 20 print(a) foo()
說明:
函數內部訪問變量a
, 先在foo
函數內部查找。因為 a
確實是在函數內部聲明的變量,然後就找到了a
3.2.2.訪問外部作用域
a = 100 def foo(): print(a) foo()
說明:
- 在
foo
函數內部,我們直接去訪問一個變量a
,那麼就會沿着作用域從內向外開始查找a
- 先查找
foo
的局部作用域,發現沒有a
。然後繼續去foo
函數的外部作用域,這個例子中就直接到了當前模塊的全局作用域,所以找到了 a, 所以就輸出了全局作用域中a
的值!
3.2.3.訪問外部函數的作用域
def outer(): a = 20 def inner(): print(a) inner() outer()
說明:
- 我們在一個函數的內部聲明了一函數,這種函數嵌套在 python 中是允許的。
- 內部函數
inner
執行的時候,訪問變量a
,現在inner
內部找變量a
, 沒有找到,然後去他外部的函數中找變量a
, 找到後, 就直接輸出了他的值
3.2.4 python 針對修改變量值的特殊情況
3.2.4.1.只能修改局部變量
在 python 的函數中, 修改一個變量的值的時候,永遠操作的是局部變量
為什麼會這樣呢?
這其實是由 python 定義變量的方式所決定的.
python 不需要顯示的去定義變量,直接賦值的時候如果變量不存在直接就定義了.
如果在函數內部可以直接修改外部作用域變量的值,則就無法定義一個同名變量了.
所以, python 才規定不能在函數內部直接修改外部作用域變量的值.
a = 10 def foo(): a = 20 # 這裡其實是新創建了一個局部變量 a .並不是修改的全局作用域的變量 a print(a) # 根據作用域的查找規則,這裡訪問的是局部變量 a foo() print(a) # 根據作用域查找規則,這裡訪問的是全局作用域的 a
3.2.4.2.變量必須先賦值才能使用
看下面的代碼:
a = 10 def foo(): a = a + 2 foo()
說明:
a = a + 2
這行代碼有問題. 為什麼?
首先要搞清楚 a + 2
中的a
是局部變量還是全局變量?
是局部變量a
!
在解釋器運行這個函數的時候, 已經檢測到函數內部有創建局部變量a
, 所以這個時候你訪問到的一定是局部變量a
.
a + 2
中的局部變量a
還沒有 被賦值,所以和 2 相加拋出了異常.UnboundLocalError
(局部變量錯誤)
a = 10 def foo(): print(a) a = 20 foo()
說明:
原因和前面的一樣的.
解析器已經檢測到你後面會聲明局部變量a
, 所以print(a)
中的 a
仍然是局部變量.但是還沒有賦值,所以就拋異常了
總結:
在函數內部如果你定義了局部變量,那麼你在任何地方都沒有辦法訪問到函數外部作用域的同名變量.
3.2.4.3 函數內修改全局變量
通過前面的學習, 正常情況下我們知道了在函數內部沒有辦法修改全局變量的值!
但是這只是正常情況下!
如果我們有在函數內部修改全局變量值的需求怎麼辦?
也是可以的, 但是我們需要做些小動作: 使用關鍵字global
a = 10 def foo(): global a # 告訴 python 解析器, a 以後就是全局變量了 a = 20 foo() print(a) # 20
說明:
global
後面跟上全局變量的名, 那麼在後面的代碼中就可以使用全局變量了.- 如果有多個全局變量需要修改,
global
可以同時定義多個全局變量.global a, b, c
3.2.4.4內部函數修改外部函數的局部變量
當用到函數嵌套的時候, 內部函數正常情況下也是無法修改外部函數的局部變量, 只能訪問讀取.
如果想修改怎麼辦?
使用關鍵字:nonlocal
a = 10 def outer(): a = 20 def inner(): nonlocal a # 把 a 綁定到外部函數的局部變量 a 上 a = 30 inner() print("outer 的局部變量a:" + str(a)) # 30 被內部函數 inner 修改了 outer() #print("全局變量a:%d"%a) #print("全局變量a:",a) print("全局變量a:" + str(a))