python 中使用 global 引發了莫名其妙的問題


 

哪里出問題了

  python 中,使用 global 會將全局變量設為本函數可用。同時,在函數內部訪問變量會先本地再全局

  在嵌套函數中,使用 global 會產生不合常理的行為。

  上代碼:

In [96]: def x():
    b = 12
    def y():
        global a,b
        a = 1
        b = 2
    y()
    print "b =",b
   ....:     

In [97]: a = 111

In [98]: del b

In [99]: x()
b = 12

In [100]: a
Out[100]: 1

In [101]: b
Out[101]: 2

 

  而在函數 x() 中,沒有用 global,此時的b使用本地。所以 print 會打印本地 b

  為什么會打印 12 ?還有 In[101]的 b 為 2 該怎么解釋?

  

  y(),使用的 global 竟然沒把 x() 的 b = 12 導進來。

  在函數 y() 中,語句  global a,b  使a,b擴展為全局,所以,在最高層,就算沒有 b(In[98]),也會產生 b(In[101])。

  

  也就是說, global a,b ,會認為 a 和 b 是最外層的變量。

  再試一下:

   

In [102]: def x():
    b = 12
    def y():
        global a,b
        a = 1
    y()      
    print "b =",b
   .....:     

In [103]: a = 111

In [104]: del b

In [105]: x()
b = 12

In [106]: a
Out[106]: 1

In [107]: b
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-107-3b5d5c371295> in <module>()
----> 1 b

NameError: name 'b' is not defined

  

  報錯了! y() global b 后沒賦值,頂層就沒有 b。這說明,global 只是引入名稱,並不做賦值等操作。

  global 不會管變量存不存在,只導入名稱,對該名稱的操作會反應到 ‘最高層名稱空間‘。

  再來:

  

In [109]: a = 111

In [110]: del b
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-110-745f2abe7045> in <module>()
----> 1 del b

NameError: name 'b' is not defined

In [111]: def x():
    b = 12
    def y():
        global a,b
        a = 1
        print b
    y()
    print "b =",b
   .....:     

In [112]: x()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-112-7354d77c61ac> in <module>()
----> 1 x()

<ipython-input-111-c05fc67a1e82> in x()
      5         a = 1
      6         print b
----> 7     y()
      8     print "b =",b
      9 

<ipython-input-111-c05fc67a1e82> in y()
      4         global a,b
      5         a = 1
----> 6         print b
      7     y()
      8     print "b =",b

NameError: global name 'b' is not defined

 

    這就確定了 內層y() 的 global 不會 導入 x() 的東西。

 

   那么,內層函數怎么使用正確的外層函數的變量呢?

 

解決內層函數參數傳遞問題

  

  一、

  首先,若只是取值,則不需要做任何處理。

 

In [119]: def x():
   .....:     a = 12
   .....:     def y():
   .....:         print a
   .....:     y()
   .....:     

In [120]: x()
12

In [121]: 

 

  在 y() 中,一旦為 a 賦值,a 立馬變內部變量。

 

In [121]: def x():
   .....:     a = 12
   .....:     def y():
   .....:         print "before a =",a
   .....:         a = 1
   .....:         print "then a =",a
   .....:     y()
   .....:     

In [122]: x()
before a =---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-122-7354d77c61ac> in <module>()
----> 1 x()

<ipython-input-121-d8fbc0dba399> in x()
      5         a = 1
      6         print "then a =",a
----> 7     y()
      8 

<ipython-input-121-d8fbc0dba399> in y()
      2     a = 12
      3     def y():
----> 4         print "before a =",a
      5         a = 1
      6         print "then a =",a

UnboundLocalError: local variable 'a' referenced before assignment

 

  一旦在函數 y() 的某處給 a 賦值,則在賦值前,python 會認為 a 不存在。

  同時發現 python2 的 print 會一個一個的 輸出。鑒於此,我又在 python3 中試了,發現他是 一起輸出。但這不是本文重點,折疊之。

 

In [7]: def x():
    a = 1
    def y():
        print("before a=",a)
        a = 10
        print("then a=",a)
    y()
   ...:     

In [8]: x()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-8-7354d77c61ac> in <module>()
----> 1 x()

<ipython-input-7-6e01e7317b24> in x()
      5         a = 10
      6         print("then a=",a)
----> 7     y()
      8 

<ipython-input-7-6e01e7317b24> in y()
      2     a = 1
      3     def y():
----> 4         print("before a=",a)
      5         a = 10
      6         print("then a=",a)

UnboundLocalError: local variable 'a' referenced before assignment
View Code

  

  同時發現 python代碼運行前 會先掃一遍代碼的,而不是單純的一行一行的執行。

  同時發現返回 UnboundLocalError,而不是 NameError。注意到 ’unbound‘,這是官方概念。用 ’unbound‘ 來描述就是:global 會將頂層變量名稱 綁定 到本地變量名稱,同時變化,是為 ’引用‘;python 檢測到 a = 1時,意識到 a 是本地的,所以 在 a ’指向一個對象‘(因為python變量均為引用),之前 ,調用 a 是非法 行為,但這種行為區別於於 NameError,就定義為 unbound local。

 

  二、

  使用 可變變量,如 list,dict

  

In [127]: def x():
   .....:     l = ["in msg"]
   .....:     def y():
   .....:         msg = l[0]
   .....:         print "msg =",msg
   .....:         l[:] = ["out msg"]
   .....:     y()
   .....:     print l[0]
   .....:     

In [128]: x()
msg = in msg
out msg

  

  沒有報錯,完美!

  要注意 語句  l[:] = ["out msg"] ,使用切片賦值,否則,

 

In [129]: def x():
    l = ["in msg"]
    def y():
        msg = l[0]
        print "msg =",msg
        l = ["out msg"]
    y()
    print l[0]
   .....:     

In [130]: x()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-130-7354d77c61ac> in <module>()
----> 1 x()

<ipython-input-129-d44e750e285f> in x()
      5         print "msg =",msg
      6         l = ["out msg"]
----> 7     y()
      8     print l[0]
      9 

<ipython-input-129-d44e750e285f> in y()
      2     l = ["in msg"]
      3     def y():
----> 4         msg = l[0]
      5         print "msg =",msg
      6         l = ["out msg"]

UnboundLocalError: local variable 'l' referenced before assignment

 

  又出 UnboundLocalError 了,因為 第六行代碼 為 l 分配了 一個新的 list。

  

  三、

  利用參數傳遞。

  

In [136]: def x():
   .....:     a, b = 1, 2
   .....:     def y(a = a, b = b):
   .....:         a, b = 3, 4
   .....:         return a, b
   .....:     a, b = y()
   .....:     print a, b
   .....:     

In [137]: x()
3 4

 

 

 

  注意,不要在默認參數上放 list等可變對象。

 四、

  python3 引入了 nonlocal 關鍵詞 來調用上一層變量。>_<

  來測試一下吧:

  

In [16]: def x():
   ....:     a = 1
   ....:     def y():
   ....:         def z():
   ....:             nonlocal a
   ....:             a = 2
   ....:         z()
   ....:     y()
   ....:     print(a)
   ....:     

In [17]: x()
2

  

  看來 nonlocal 是依次向上申請名稱。

  

In [25]: def x():
    a = 2
    def y():
        a = 3
        def z():
            nonlocal a
            print("in z a =",a)
            a = 4
        z()
        print("in y a =", a)
    y()
    print("in x a =", a)
   ....:     

In [26]: x()
in z a = 3
in y a = 4
in x a = 2

In [27]: 

  

  至此,問題完美解決。

  原創,歡迎指正。

  本文完。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM