python的作用域问题

查看:95
本文介绍了python的作用域问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问 题

x = 3
y = [3]

def test1():
    x += 1
    print x

def test2():
    y[0] = 4
    y.append(5)
    print y

test2()
test1()

这段代码执行结果: test2()成功打印[4, 5], test1()却报错: UnboundLocalError: local variable 'x' referenced before assignment

想不明白,为什么会这样,全局变量在函数里可以直接打印,但是如果要改变它的值,会报错,但是test2()为什么不报错?如果把y换成dict类型,就能在函数里不需要用global声明,可以直接改变y的值,但如果是str或number,就会报错,为什么?

解决方案

其實這個問題可以先放下變量參考的對象是可變還是不可變這件事情, 我們要注意的是變量的定位, 定義的時間點和作用域。

考慮以下代碼:

def test(a):
    print(a)
    print(b)
    
test(1)

這段代碼會引發一個錯誤:

1
Traceback (most recent call last):
  File "tp.py", line 5, in <module>
    test(1)
  File "tp.py", line 3, in test
    print(b)
NameError: name 'b' is not defined

a 是 function test 的 param variable, 所以它屬於一個 local variable, 他的作用域是 test 函數, 我們將 1 傳進去讓 a 參考, 所以 print(a) 沒有什麼大問題。
但是 b 從頭到尾都沒定義, 即使依據 LEGB 原則去尋找也遍尋不著, 所以 raise 了一個 NameError

要解決這個問題, 也許我們可以定義一個 global variable:

b = 100

def test(a):
    print(a)
    print(b)
    
test(1)

很好, 這次看起來沒什麼問題, 因為 Python 在 global 的作用域中找到了 b, Python 之所以會使用 global b 那是因為在 local 我們並沒有定義 b

接著我們開始在函數裡面對變量賦值:

b = 100

def test(a):
    b = 20
    print(a)
    print(b)
    
test(1)
print(b)

結果:

1
20
100

函數內的兩個 print 不令人意外地印出了 1 跟 20, 但是為什麼離開 function 之後印出 b 的值是 100 呢?
因為我們在函數中寫了這樣一個 賦值 定義 b = 20, 所以在 test 中看到的 b 都是 local 的, 不是 global 的, 所以我們對 b 造成的任何更動都不會影響到 global b

這告訴我們:

當 local variable 沒有被定義時, Python 會自動去使用 global variable, 反之則不會

那我們要怎麼樣才能在函數內對 global 的變量進行操作呢? 這就需要 global 這個關鍵字的輔助了:

b = 100

def test(a):
    global b
    b = 20
    print(a)
    print(b)
    
test(1)
print(b)

1
20
20

有了 global 關鍵字 對 b 做了說明, Python 會將 test 中的 b 當作是要存取 global 的變量 bb = 20 僅會被當作一個賦值動作而不會定義一個新的 local variable。

接著讓我們來看一個比較令人疑惑的例子:

b = 100

def test(a):
    print(a)
    print(b)
    b = 20
    
test(1)
print(b)

結果:

1
Traceback (most recent call last):
  File "tp.py", line 8, in <module>
    test(1)
  File "tp.py", line 5, in test
    print(b)
UnboundLocalError: local variable 'b' referenced before assignment

這邊出現了 UnboundLocalError, 為什麼會這樣呢? 原因很簡單, b 在這個函數內出現了賦值兼定義的動作: b = 20, 所以 test 內的 b 都是 local 的(在這裡並沒有使用 global 來指明 b 是 global 的), 所以當 print(b) 的時候, Python 會試圖去抓 local b 而不是 global b, 但是悲劇的是, 在這一步, local b 還沒被賦值, 所以才會說 local variable 'b' referenced before assignment 在被賦值前被參考了, 那自然是不行的了。

回過頭來看你給的例子:

x = 3

def test1():
    x += 1
    print x

在這裡, test1 中有 x 的定義發生 (沒有 global, 且 x 出現在等號左邊), 自然在參考 x 的時候, 會想要取用 local 的, 但是因為:

x += 1 等義 x = x + 1

所以在等號右邊想要取得 x 參考的值時, 卻發現他還沒被賦值呢! 那這就跟上面一個例子一樣:

UnboundLocalError: local variable 'x' referenced before assignment

至於你的 test2 不會出現問題:

y = [3]

def test2():
    y[0] = 4
    y.append(5)
    print y

是因為 y 沒有在 test2 中出現在等號左邊, 所以 Python 自動認定使用 global 的 y 那自然是沒問題了!

結論

  1. 觀察一個 function 內有沒有定義 local vairable 就看該 variable name 有沒有出現在等號左邊, 且該 variable name 沒有被 global 指明

  2. 若有定義 local variable 則 Python 將不會去尋找 global variable, 否則 Python 會自動取用 global vairable

  3. 如果有定義 local vairable 但是你在該 variable 被賦值前就去讀取參考的話, 就會有 UnboundLocalError 出現

  4. 要解決這個問題, 請記得加註 global 指明全域, 否則不該有這種寫法出現 (in-place 操作, 或是後面才賦值)

  5. 類似的問題也會出現在 local function 中, 解決的辦法是加註 nonlocal (Python3), 不過那又是另一個故事了。


我回答過的問題: Python-QA

这篇关于python的作用域问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆