1. 程式人生 > >Python第六章-函式02-函式的作用域

Python第六章-函式02-函式的作用域

# 函式 ## 三、作用域規則 有了函式之後,我們必須要面對一個作用域的問題。 比如:你現在訪問一個變數,那麼 python 解析器是怎麼查詢到這個變數,並讀取到這個變數的值的呢? 依靠的就是作用域規則! ### 3.1 作用域 作用域(`scope`) 作用域就是 python 程式的一塊文字區域,在這個區域內,可以直接訪問(Directly accessible)名稱空間。 直接訪問的意思就是:當你訪問一個絕對的命名的時候,直接在名稱空間中查詢 儘管作用域的定義是靜態的,但是作用域的使用(查詢變數)卻是動態的。 ------ 在程式碼執行的任何時間,至少有 3 個巢狀的作用域,這些作用域的名稱空間可以直接訪問。 1. 內部作用域(區域性作用域)。包含了所有的區域性命名,在訪問變數的時候,首先在內部作用域中查詢。 2. 然後是巢狀函式的外層作用域。在這裡搜尋非區域性,但也是非全域性的命名。(在 python 中允許在函式中定義函式的) 3. 然後是包含當前模組的全域性作用域。 4. 最後搜尋的是最外層的建立內建命名的作用域。 ------ > ### 作用域搜尋規則:LEGB > > L:區域性的(local) > > E:封閉的(Enclosing) > > G:全域性的(Global) > > B:內建的(Built-in) > > #### 一、區域性名稱空間 > > 函式內部的名稱空間,在呼叫函式的時候生成,呼叫結束時消失。當局部名稱空間有效時,它是第一個用於檢查某個名字存在性的名稱空間。如果在區域性名稱空間內找到該名稱,則返回與名字相關聯的物件,反之提示出錯。 ### 3.2作用域在 python 中的具體應用 #### 3.2.1.訪問區域性作用域 ```python def foo(): a = 20 print(a) foo() ``` 說明: 函式內部訪問變數`a`, 先在`foo`函式內部查詢。因為 `a`確實是在函式內部宣告的變數,然後就找到了`a` ------ #### 3.2.2.訪問外部作用域 ```python a = 100 def foo(): print(a) foo() ``` 說明: 1. 在`foo`函式內部,我們直接去訪問一個變數 `a`,那麼就會沿著作用域從內向外開始查詢`a` 2. 先查詢`foo`的區域性作用域,發現沒有`a`。然後繼續去`foo`函式的外部作用域,這個例子中就直接到了當前模組的全域性作用域,所以找到了 a, 所以就輸出了全域性作用域中`a`的值! ------ #### 3.2.3.訪問外部函式的作用域 ```python def outer(): a = 20 def inner(): print(a) inner() outer() ``` 說明: 1. 我們在一個函式的內部聲明瞭一函式,這種函式巢狀在 python 中是允許的。 2. 內部函式`inner`執行的時候,訪問變數`a`,現在`inner`內部找變數`a`, 沒有找到,然後去他外部的函式中找變數`a`, 找到後, 就直接輸出了他的值 ------ #### 3.2.4 python 針對修改變數值的特殊情況 ##### 3.2.4.1.只能修改區域性變數 **在 python 的函式中, 修改一個變數的值的時候,永遠操作的是區域性變數** 為什麼會這樣呢? 這其實是由 python 定義變數的方式所決定的. python 不需要顯示的去定義變數,直接賦值的時候如果變數不存在直接就定義了. 如果在函式內部可以直接修改外部作用域變數的值,則就無法定義一個同名變量了. 所以, python 才規定不能在函式內部直接修改外部作用域變數的值. ------ ```python a = 10 def foo(): a = 20 # 這裡其實是新建立了一個區域性變數 a .並不是修改的全域性作用域的變數 a print(a) # 根據作用域的查詢規則,這裡訪問的是區域性變數 a foo() print(a) # 根據作用域查詢規則,這裡訪問的是全域性作用域的 a ``` ![](https://img2020.cnblogs.com/blog/1988132/202004/1988132-20200401190144370-625901493.png) ------ ##### 3.2.4.2.變數必須先賦值才能使用 看下面的程式碼: ```python a = 10 def foo(): a = a + 2 foo() ``` ![](https://img2020.cnblogs.com/blog/1988132/202004/1988132-20200401190155730-120535102.png) **說明:** `a = a + 2` 這行程式碼有問題. 為什麼? 首先要搞清楚 `a + 2` 中的`a`是區域性變數還是全域性變數? **是區域性變數`a`!** 在直譯器執行這個函式的時候, 已經檢測到函式內部有建立區域性變數`a`, 所以這個時候你訪問到的一定是區域性變數`a`. `a + 2` 中的區域性變數`a`還沒有 被賦值,所以和 2 相加丟擲了異常.`UnboundLocalError`(區域性變數錯誤) ------ ```python a = 10 def foo(): print(a) a = 20 foo() ``` ![](https://img2020.cnblogs.com/blog/1988132/202004/1988132-20200401190209020-384800960.png) 說明: 原因和前面的一樣的. 解析器已經檢測到你後面會宣告區域性變數`a`, 所以`print(a)`中的 `a` 仍然是區域性變數.但是還沒有賦值,所以就拋異常了 ------ **總結:** 在函式內部如果你定義了局部變數,那麼你在任何地方都沒有辦法訪問到函式外部作用域的同名變數. ------ ##### 3.2.4.3 函式內修改全域性變數 通過前面的學習, 正常情況下我們知道了在函式內部沒有辦法修改全域性變數的值! **但是這只是正常情況下!** 如果我們有在函式內部修改全域性變數值的需求怎麼辦? **_也是可以的, 但是我們需要做些小動作: 使用關鍵字`global`_** ------ ```python a = 10 def foo(): global a # 告訴 python 解析器, a 以後就是全域性變量了 a = 20 foo() print(a) # 20 ``` **說明:** 1. `global` 後面跟上全域性變數的名, 那麼在後面的程式碼中就可以使用全域性變量了. 2. 如果有多個全域性變數需要修改, `global`可以同時定義多個全域性變數.`global a, b, c` ------ ##### 3.2.4.4內部函式修改外部函式的區域性變數 當用到函式巢狀的時候, 內部函式正常情況下也是無法修改外部函式的區域性變數, 只能訪問讀取. 如果想修改怎麼辦? 使用關鍵字:`nonlocal` ```python 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)) ``` ![](https://img2020.cnblogs.com/blog/1988132/202004/1988132-20200401190226129-809184