天天看點

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.通路局部作用域

def foo():
    a = 20
    print(a)

foo()
           

說明:

函數内部通路變量

a

, 先在

foo

函數内部查找。因為

a

确實是在函數内部聲明的變量,然後就找到了

a

3.2.2.通路外部作用域

a = 100


def foo():
    print(a)


foo()
           

說明:

  1. foo

    函數内部,我們直接去通路一個變量

    a

    ,那麼就會沿着作用域從内向外開始查找

    a

  2. 先查找

    foo

    的局部作用域,發現沒有

    a

    。然後繼續去

    foo

    函數的外部作用域,這個例子中就直接到了目前子產品的全局作用域,是以找到了 a, 是以就輸出了全局作用域中

    a

    的值!

3.2.3.通路外部函數的作用域

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 才規定不能在函數内部直接修改外部作用域變量的值.

a = 10


def foo():
    a = 20 # 這裡其實是新建立了一個局部變量 a .并不是修改的全局作用域的變量 a

    print(a) # 根據作用域的查找規則,這裡通路的是局部變量 a


foo()
print(a) # 根據作用域查找規則,這裡通路的是全局作用域的 a
           
Python第六章-函數02-函數的作用域
3.2.4.2.變量必須先指派才能使用

看下面的代碼:

a = 10


def foo():
    a = a + 2

foo()
           
Python第六章-函數02-函數的作用域

說明:

a = a + 2

這行代碼有問題. 為什麼?

首先要搞清楚

a + 2

中的

a

是局部變量還是全局變量?

是局部變量

a

!

在解釋器運作這個函數的時候, 已經檢測到函數内部有建立局部變量

a

, 是以這個時候你通路到的一定是局部變量

a

.

a + 2

中的局部變量

a

還沒有 被指派,是以和 2 相加抛出了異常.

UnboundLocalError

(局部變量錯誤)

a = 10


def foo():
    print(a)
    a = 20


foo()
           
Python第六章-函數02-函數的作用域

說明:

原因和前面的一樣的.

解析器已經檢測到你後面會聲明局部變量

a

, 是以

print(a)

中的

a

仍然是局部變量.但是還沒有指派,是以就抛異常了

總結:

在函數内部如果你定義了局部變量,那麼你在任何地方都沒有辦法通路到函數外部作用域的同名變量.

3.2.4.3 函數内修改全局變量

通過前面的學習, 正常情況下我們知道了在函數内部沒有辦法修改全局變量的值!

但是這隻是正常情況下!

如果我們有在函數内部修改全局變量值的需求怎麼辦?

也是可以的, 但是我們需要做些小動作: 使用關鍵字

global

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

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))
           
Python第六章-函數02-函數的作用域