有這樣一個函數(shù):
def outside():
x=[]
print(id(x))
def inside():
print(id(x))
x[:]=[1,2,3]
print(id(x))
inside()
print(id(x))
print(x)
調(diào)用后沒出現(xiàn)問題,輸出:
140560473157960
140560473157960
140560473157960
140560473157960
[1, 2, 3]
但是將里面的x換成一個字符串,如下:
def outside():
x='outside'
print(id(x))
def inside():
print(id(x))
x='inside'
print(id(x))
inside()
print(id(x))
print(x)
再來進(jìn)行調(diào)用的時候就變成了:
140560473762872
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in outside
File "<stdin>", line 5, in inside
UnboundLocalError: local variable 'x' referenced before assignment
按照規(guī)則,此時進(jìn)入inside
函數(shù)里,為什么x不指向原來的'outside'
字符串了呢?
未定義的變量不是應(yīng)該產(chǎn)生NameError
嗎,為什么此處不是?
我原來以為字符串和list都是類似于C中的指針,但現(xiàn)在看來不是這樣,如果可以的話希望能對這一點進(jìn)行一些介紹,謝謝。
從易到難一個個回答, 因為是個人愚見, 所以如果有大神看到說得不正確, 希望能夠指出
類似C中的指針: 這個是正確的, 因為在py
里面, 幾乎所有的事物都是對象, 就連變量賦值, 也是先生成對象
, 再讓變量
指向這個對象
,而對象還分可變對象
和不可變對象
, 在對可變對象
操作時, 是會影響到其他指向這個對象的變量, 例如:
o = [1, 2, 3, 4]
b = o
print id(o)
print id(b)
b[1] = 123123
print b
print o
輸出:
39946376
39946376
[1, 123123, 3, 4]
[1, 123123, 3, 4] # o指向的列表也被改變
而對于不可變對象
, 是直接就放棄舊的對象, 而指向新的對象, 例如:
s = '123123'
print id(s)
s = '32131'
print id(s)
# 輸出:
41392768
41392808
所以你在操作python對象時, 需要謹(jǐn)記該對象是屬于哪種類型, 你的操作又會不會因為這些特性而失敗或者沒達(dá)到自己想要的效果.
未定義的變量: python在查找變量時, 將遵循LEGB的順序, 只有都查找完畢還是沒找到的情況下, 才會觸發(fā)NameError
異常, 這個可以參考我的一篇博文: Python: 作用域(scope) 和 LEGB
UnboundLocalError: 這個問題是最常見, 也是最難解釋的, 因為我們總是相當(dāng)然地覺得, 它必定就會根據(jù)ELGB的順序去查到變量;其實我們的理解并沒錯誤, 只是我們忽略了一點:賦值語句
,如果函數(shù)代碼段沒有賦值語句, 那么這個問題是不會出現(xiàn), 但為什么出現(xiàn)賦值語句就會報錯呢? 這和python的作用域有關(guān), 在上面那篇文章提到, python作用域并不是動態(tài)的,而是靜態(tài)的, 從腳本文件的縮進(jìn), 就能看出來的, 所以在代碼:
x='outside'
print(id(x))
def inside():
print(id(x))
x='inside'
print(id(x))
在inside
中, 已經(jīng)有了賦值語句, 所以對于x
,他已經(jīng)不會從enclosing
或者global
甚至bulitin
里面去查找, 它已經(jīng)被認(rèn)定在local
域了, 只是這個值并沒有和真正的對象'inside'
建立起綁定關(guān)系, 因為代碼沒有運(yùn)行到真正的賦值語句, 所以, 會觸發(fā)這個UnboundLocalError
. 而為什么那個列表會可以那樣做, 因為他們兩個是完全不同的操作, 同樣都是print(id(x))list的操作字節(jié)碼是LOAD_DEREF
, 而字符串的操作字節(jié)碼是LOAD_FAST
, 而x[:]=[1,2,3]/x='inside'分別對應(yīng)的字節(jié)碼又是STORE_SLICE+3
和 STORE_FAST
, 前者是在原來的基礎(chǔ)上修改, 而后者是重新指向新的對象, 而這兩種方式的區(qū)別, 決定了,它們在構(gòu)建函數(shù)時, 以怎樣的形式存放x
, 這個就涉及到python函數(shù)
構(gòu)建的原理了, 有興趣可以看看源碼中的object/ceval.c源碼
, 這是虛擬機(jī)運(yùn)行的原理, 關(guān)于這個問題可以簡單看我另一篇文章, 比較簡單將UnboundLocalError
: 說下那神奇的 UnboundLocalError: local variable x referenced before assignment
你在inside函數(shù)里面重新賦值了新的變量x,兩個x的作用域是不相同的。
而導(dǎo)致UnboundLocalError,是因為你在inside的作用域里面,打印了一個沒有初始化的變量。
具體看這個解釋:
https://docs.python.org/2/faq...
基本上Lin_R已經(jīng)說的很清楚了。
outside函數(shù)與inside函數(shù),他們的域是不同的。由于你在inside函數(shù)中對x進(jìn)行了賦值,當(dāng)在inside函數(shù)中使用變量x時,此時x就被認(rèn)定是在inside的local域中。此時的x是不會使用其他域中的值的。所以在print(x)時,由于x沒有初始化的值,因此出錯。雖然在c中可以使用已定義而未賦值的變量,但是python不允許這種情況。
在python3中,有一個nonlocal語句可以解決這個問題。
def outside():
x='outside'
print(id(x))
def inside():
nonlocal x
print(id(x))
x='inside'
print(id(x))
inside()
print(id(x))
print(x)
注意,此時使用global
語句是不行的,因為在global域內(nèi)沒有x這個變量。
微信掃碼
關(guān)注PHP中文網(wǎng)服務(wù)號
QQ掃碼
加入技術(shù)交流群
Copyright 2014-2025 http://www.miracleart.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號