『無為則無心』Python函式 — 30、Python變數的作用域

繁華似錦Fighting發表於2022-01-05

1、作用於的概念

變數作用域指的是變數生效的範圍,在Python中一共有兩種作用域。

  • 全域性作用域
    • 全域性作用域在程式執行時建立,在程式執行結束時銷燬。
    • 所有函式以外的區域都是全域性作用域。
    • 在全域性作用域中定義的變數,都屬於全域性變數,全域性變數可以在程式的任意位置被訪問。
  • 函式作用域
    • 函式作用域在函式呼叫時建立,在呼叫結束時銷燬。
    • 函式每呼叫一次就會產生一個新的函式作用域(不呼叫不產生)。
    • 在函式作用域中定義的變數,都是區域性變數,它只能在函式內部被訪問。

2、區域性變數

所謂區域性變數是定義在函式體內部的變數,即只在函式體內部生效。

def testA():
    # 區域性變數a
    # 在函式中為變數賦值時,預設都是為區域性變數賦值
    # 區域性變數不會影響函式外的變數。
    a = 100
    # 函式體內部訪問,能訪問到a變數
    print(a)


testA()  # 100
print(a)  # 報錯:name 'a' is not defined

變數a是定義在testA函式內部的變數,在函式外部訪問則立即報錯。

區域性變數的作用:在函式體內部,臨時儲存資料,即當函式呼叫完成後,則銷燬區域性變數。

3、全域性變數

所謂全域性變數,指的是在函式體內、外都能生效的變數。

思考:如果有一個資料,在函式A和函式B中都要使用,該怎麼辦?

答:將這個資料儲存在一個全域性變數裡面。

# 定義全域性變數a
a = 100

def testA():
    print(a)  # 訪問全域性變數a,並列印變數a儲存的資料

def testB():
    print(a)  # 訪問全域性變數a,並列印變數a儲存的資料

testA()  # 100
testB()  # 100

思考:testB函式需求修改變數a的值為200,如何修改程式?

a = 100

def testA():
    print(a)

def testB():
    a = 200
    print(a)

testA()  # 100
testB()  # 200
print(f'全域性變數a = {a}')  # 全域性變數a = 100

思考:在testB函式內部的a = 200中的變數a是在修改全域性變數a嗎?

答:不是。觀察上述程式碼發現,15行得到a的資料是100,仍然是定義全域性變數a時候的值,而沒有返回

testB函式內部的200。綜上:testB函式內部的a = 200是定義了一個區域性變數。

(1)global關鍵字

思考:如何在函式體內部修改全域性變數?

a = 100

def testA():
    print(a)

def testB():
    # 想要修改全域性變數a的值是200
    # global 關鍵字宣告a是全域性變數
    global a
    a = 200
    print(a)


testA()  # 100
testB()  # 200
print(f'全域性變數a = {a}')  # 全域性變數a = 200

global關鍵字的作用是,在函式內部宣告一個變數為全域性變數。換句話說如果希望在函式內部修改全域性變數,則需要使用global關鍵字來宣告變數。

(2)總結

  • 如果在函式裡面直接把變數a=200賦值,此時的a不是全域性變數的修改,而是相當於在函式內部宣告瞭一個新的區域性變數。
  • 函式體內部修改全域性變數: 先global宣告a為全域性變數,然後再變數重新賦值。

4、變數的查詢

當我們使用變數時,會優先在當前作用域中尋找該變數,如果有則使用,

如果沒有則繼續去上一級作用域中尋找,如果有則使用,

如果依然沒有則繼續去上一級作用域中尋找,以此類推。

直到找到全域性作用域,依然沒有找到,則會丟擲異常 NameError: name 'a' is not defined

# 練習說明
a = 10
def fn2():
    def fn3():
        a = 30
        print('fn3中:','a =',a)
    fn3()
    print('fn2中:','a =',a)

fn2()
"""
輸出結果:
fn3中: a = 30
fn2中: a = 10
"""

5、作用域中可變資料型別變數

c = 10
def fn4(a):
    # 在函式中對形參進行重新賦值,不會影響其他的變數
    a = 20
    print('a =', a, id(a))

fn4(c)
print('c =', c, id(c))
"""
輸出結果:
a = 20 8791349231264
c = 10 8791349230944
"""


# 如果形參接收到的資料是一個全域性列表
# 當在函式內嘗試修改列表中的元素時,全域性列表的資料也會發生改變
c = [1,2,3]
def fn4(a):
    # 如果形參執行的是一個物件,當我們通過形參去修改物件時
    # 會影響到所有指向該物件的變數
    a[0] = 100
    print('a =', a, id(a))

fn4(c)
print('c =', c, id(c))
"""
輸出結果:
a = [100, 2, 3] 5132808
c = [100, 2, 3] 5132808
"""


# 如果我們不向全域性變數有所改動
# 就需要使用我們之前學過的淺複製,
# 或者傳入一個切片,就可以解決
c = [1, 2, 3]

def fn4(a):
    # 在函式中對形參進行重新賦值,不會影響其他的變數
    a[0] = 100
    print('a =', a, id(a))

fn4(c.copy())
# fn4(c[:])
print('c =', c, id(c))
"""
輸出結果:
a = [100, 2, 3] 6050824
c = [1, 2, 3] 6050312
"""

6、多函式程式執行流程

一般在實際開發過程中,一個程式往往由多個函式組成,並且多個函式共享某些資料,如下所示:

(1)共用全域性變數

# 1. 定義全域性變數
glo_num = 0

def test1():
    global glo_num
    # 修改全域性變數
    glo_num = 100

def test2():
    # 呼叫test1函式中修改後的全域性變數
    print(glo_num)


# 2. 呼叫test1函式,執行函式內部程式碼:宣告和修改全域性變數
test1()
# 3. 呼叫test2函式,執行函式內部程式碼:列印
test2()  # 100

(2)返回值作為引數傳遞

# 先得到函式一的返回值,再把這個返回值傳入到函式二
def test1():
    return 50

def test2(num):
    print(num)


# 1. 儲存函式test1的返回值
result = test1()

# 2.將函式返回值所在變數作為引數傳遞到test2函式
test2(result)  # 50

相關文章