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,就会报错,为什么?
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
其實這個問題可以先放下變量參考的對象是可變還是不可變這件事情, 我們要注意的是變量的定位, 定義的時間點和作用域。
考慮以下代碼:
這段代碼會引發一個錯誤:
a是 functiontest的 param variable, 所以它屬於一個 local variable, 他的作用域是test函數, 我們將 1 傳進去讓 a 參考, 所以print(a)沒有什麼大問題。但是
b從頭到尾都沒定義, 即使依據 LEGB 原則去尋找也遍尋不著, 所以 raise 了一個NameError。要解決這個問題, 也許我們可以定義一個 global variable:
很好, 這次看起來沒什麼問題, 因為 Python 在 global 的作用域中找到了
b, Python 之所以會使用 globalb那是因為在 local 我們並沒有定義b。接著我們開始在函數裡面對變量賦值:
結果:
函數內的兩個
print不令人意外地印出了 1 跟 20, 但是為什麼離開 function 之後印出b的值是 100 呢?因為我們在函數中寫了這樣一個 賦值 兼 定義
b = 20, 所以在test中看到的b都是 local 的, 不是 global 的, 所以我們對b造成的任何更動都不會影響到 globalb。這告訴我們:
那我們要怎麼樣才能在函數內對 global 的變量進行操作呢? 這就需要
global這個關鍵字的輔助了:有了
global關鍵字 對b做了說明, Python 會將test中的b當作是要存取 global 的變量b而b = 20僅會被當作一個賦值動作而不會定義一個新的 local variable。接著讓我們來看一個比較令人疑惑的例子:
結果:
這邊出現了
UnboundLocalError, 為什麼會這樣呢? 原因很簡單,b在這個函數內出現了賦值兼定義的動作:b = 20, 所以test內的b都是 local 的(在這裡並沒有使用 global 來指明b是 global 的), 所以當print(b)的時候, Python 會試圖去抓 localb而不是 globalb, 但是悲劇的是, 在這一步, localb還沒被賦值, 所以才會說local variable 'b' referenced before assignment在被賦值前被參考了, 那自然是不行的了。回過頭來看你給的例子:
在這裡,
test1中有x的定義發生 (沒有 global, 且x出現在等號左邊), 自然在參考x的時候, 會想要取用 local 的, 但是因為:所以在等號右邊想要取得
x參考的值時, 卻發現他還沒被賦值呢! 那這就跟上面一個例子一樣:至於你的
test2不會出現問題:是因為 y 沒有在
test2中出現在等號左邊, 所以 Python 自動認定使用 global 的y那自然是沒問題了!結論
觀察一個 function 內有沒有定義 local vairable 就看該 variable name 有沒有出現在等號左邊, 且該 variable name 沒有被
global指明若有定義 local variable 則 Python 將不會去尋找 global variable, 否則 Python 會自動取用 global vairable
如果有定義 local vairable 但是你在該 variable 被賦值前就去讀取參考的話, 就會有 UnboundLocalError 出現
要解決這個問題, 請記得加註
global指明全域, 否則不該有這種寫法出現 (in-place 操作, 或是後面才賦值)類似的問題也會出現在 local function 中, 解決的辦法是加註
nonlocal(Python3), 不過那又是另一個故事了。我回答過的問題: Python-QA
python 的变量作用域是这样规定的,test1中可以
print x但是却不能修改 x有人说 “局部作用域中全局变量应是只读” ,但是对于引用型变量又不满足这个条件。。。
python 的这个地方确实绕人
简单的说,局部作用域中不能改变全局变量的绑定,在
CPython也就是不能改变变量的地址。参看:Python程序员最常犯的十个错误 中的 常见错误4:错误理解Python中的变量名解析
你的
test1函数有赋值操作x += 1, 被Python解释器认为是函数本地作用域的变量, 但是该变量x在赋值之前并没有被定义, 而且x是number, 属于不可变类型, 针对不可变类型的赋新值, 需要重新创建一个不可变类型的对象,并将原来的变量重新指向新创建的对象, 但是这个新创建的对象在LEGB中都没有找到, 所以会报错test2函数中,y是个list, 属于可变类型, list在append之后,还是指向同个内存地址,因为list是可变类型,可以在原处修改, 所以并不会报错Python中所有赋值操作基本都分为下面三个步骤(以a = 3为例):创建一个对象来代表该值
3创建一个变量
a, 如果他还没被创建的话将变量
a与新的对象3相连接我们观察下你这
test2函数在执行过程中变量y的内存地址变化输出
可以看到
y其实是在原地修改的, 所以不需要重复创建变量; 而对于x不可变类型来说, 要给他赋值, 就必须先创建一个新的对象, 即使名字一样, 可以看到其实如果每次不同的赋值, 实际上x的内存地址并不相同, 这也就可以解释str或number都会报错了