Python第六章-函式02-函式的作用域
阿新 • • 發佈:2020-04-01
# 函式
## 三、作用域規則
有了函式之後,我們必須要面對一個作用域的問題。
比如:你現在訪問一個變數,那麼 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