關於python變數的作用域問題
给我你的怀抱
给我你的怀抱 2017-05-18 10:53:10
0
3
708

有這樣一個函數:

def outside():
    x=[]
    print(id(x))
    def inside():
        print(id(x))
        x[:]=[1,2,3]
        print(id(x))
    inside()
    print(id(x))
    print(x)

呼叫後沒出現問題,輸出:
140560473157960
140560473157960
140560473157960
140560473157960473157960
140560473157960473157960

14056047315796047311, 22,222, 22, 22,22260

#但是將裡面的x換成字串,如下:

def outside():
    x='outside'
    print(id(x))
    def inside():
        print(id(x))
        x='inside'
        print(id(x))
    inside()
    print(id(x))
    print(x)

再來進行呼叫的時候就變成了:

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
  • 依照規則,此時進入inside函數裡,為什麼x不指向原來的'outside'字串了呢?

  • 未定義的變數不是應該產生NameError嗎,為什麼這裡不是?

  • 我原本以為字串和list都是類似C中的指針,但現在看來不是這樣,如果可以的話希望能對這一點進行一些介紹,謝謝。

给我你的怀抱
给我你的怀抱

全部回覆(3)
我想大声告诉你

從易到難一個個回答, 因為是個人愚見, 所以如果有大神看到說得不正確, 希望能夠指出
類似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物件時, 需要謹記該物件是屬於哪種類型, 你的操作又會不會因為這些特性而失敗或沒達到自己想要的效果.

未定義的變數: python在查找變數時, 將遵循LEGB的順序, 只有都查找完畢還是沒找到的情況下, 才會觸發NameError異常, 這個可以參考我的一篇博文: Python: 作用域(scope) 和LEGB

UnboundLocalError: 這個問題是最常見, 也是最難解釋的, 因為我們總是相當然地覺得, 它必定就會根據ELGB的順序去查到變量;其實我們的理解並沒錯誤, 只是我們忽略了一點:赋值语句,如果函數代碼段沒有賦值語句, 那麼這個問題是不會出現, 但為什麼出現賦值語句就會報錯呢? 這和python的作用域有關, 在上面那篇文章提到, python作用域不是動態的,而是靜態的, 從腳本文件的縮排, 就能看出來的, 所以在代碼:

 x='outside'
    print(id(x))
    def inside():
        print(id(x))
        x='inside'
        print(id(x))

inside中, 已经有了赋值语句, 所以对于x,他已经不会从enclosing 或者global甚至bulitin里面去查找, 它已经被认定在local域了, 只是这个值并没有和真正的对象'inside'建立起绑定关系, 因为代码没有运行到真正的赋值语句, 所以, 会触发这个UnboundLocalError. 而为什么那个列表会可以那样做, 因为他们两个是完全不同的操作, 同样都是print(id(x))list的操作字节码是LOAD_DEREF, 而字符串的操作字节码是LOAD_FAST, 而x[:]=[1,2,3]/x='inside'分别对应的字节码又是STORE_SLICE+3STORE_FAST, 前者是在原来的基础上修改, 而后者是重新指向新的对象, 而这两种方式的区别, 决定了,它们在构建函数时, 以怎样的形式存放x, 这个就涉及到python函数构建的原理了, 有兴趣可以看看源码中的object/ceval.c源码, 这是虚拟机运行的原理, 关于这个问题可以简单看我另一篇文章, 比较简单将UnboundLocalError: 說下那神奇的 UnboundLocalError: local variable x referenced before assignment

某草草

你在inside函數裡面重新賦值了新的變數x,兩個x的作用域是不相同的。
而導致UnboundLocalError,是因為你在inside的作用域裡面,印了一個沒有初始化的變數。
具體看這個解釋:
https://docs.python.org/2/faq...

洪涛

基本上Lin_R已經說的很清楚了。
outside函數與inside函數,他們的域是不同的。由於你在inside函數中對x進行了賦值,當在inside函數中使用變數x時,此時x就被認定是在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域內沒有x這個變數。

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!