python 本地變量和全局變量 locals() globals() global nonlocal 閉包 以及和 scala 閉包的區別


最近看 scala ,看到了它的作用域,特此回顧一下python的變量作用域問題。

A = 10
B = 100
print A  #10
print globals()  #{'A': 10, 'B': 100, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'E:/PycharmProjects/untitled/test1.py', '__package__': None, '__name__': '__main__', '__doc__': None}

def mu(x,y):
    B = 9 
    print locals()  #{'y': 9, 'x': 10,'B':9}
    if A == 10:
        B = 19
        print locals() #{'y':9,'x':10,'B':19}
        return x+y
    else:
        return x-y

m = mu(10,9)
print(m)  # 19
print locals()  #{'A': 10, 'B': 100, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'E:/PycharmProjects/untitled/test1.py', 'm': 19, '__package__': None, 'mu': <function mu at 0x02ADA430>, '__name__': '__main__', '__doc__': None}
print globals()#{'A': 10, 'B': 100, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'E:/PycharmProjects/untitled/test1.py', 'm': 19, '__package__': None, 'mu': <function mu at 0x02ADA430>, '__name__': '__main__', '__doc__': None}  # 全局作用域的 locals 和globals 內的變量內容是一樣的。
print __name__  #  __main__      
print __file__   #E:/PycharmProjects/untitled/test1.py
print __doc__ # None
print __package__ # None

print B  # 100

global


A = 10
B = 100
print A # 10
print globals()#{'A': 10, 'B': 100, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'E:/PycharmProjects/untitled/test1.py', '__package__': None, '__name__': '__main__', '__doc__': None}

def mu(x,y):
    global B
    B +=1
    print(B)  #101
    print locals()  #{'y': 9, 'x': 10}   進過global 作用, 本地變量中不會創建新的本地變量 B 了。而是直接用的全局的 B。
    if A == 10:
        B = 20
        print locals()  #{'y': 9, 'x': 10}  
        return x+y
    else:
        return x-y

m = mu(10,9)
print(m)
print locals() #{'A': 10, 'B': 20, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'E:/PycharmProjects/untitled/test1.py', 'm': 19, '__package__': None, 'mu': <function mu at 0x02A8A430>, '__name__': '__main__', '__doc__': None}

print globals()#{'A': 10, 'B': 20, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'E:/PycharmProjects/untitled/test1.py', 'm': 19, '__package__': None, 'mu': <function mu at 0x02A8A430>, '__name__': '__main__', '__doc__': None}

print B#20 因為函數中使用了 global 關鍵字, 函數中使用的是全局變量B ,並且對其做出了修改,所以這個B也就變成了 20

賦值 操作

A = 10
B = 100
print A
print globals()

def mu(x,y):
    # B  = B +1 # 這么做會報錯 因為賦值操作會查找 本地變量 然而本地變量中並沒有B 變量  並不是想象中的     B =  100 +1 ,只要出現賦值語句,就會遮蔽外層變量,而在此之前,本地變量中並未定義B。
# print(B)   #100
    print locals()
    if A == 10:
        if B == 100:   #這么做不會報錯,它會查找到全局變量
            print "haha"
            print locals()  #{'y': 9, 'x': 10}
        return x+y
    else:
        return x-y

m = mu(10,9)
print(m)
print locals()
print globals()
print B  #100

以下為python3 操作

>>> def a(x):
...     print(x)
...     def b():
...         print(x)  # 參數作用域,父函數的參數可以 被子函數所使用,這也就是閉包。scala 作用域與此效果類似,內部函數,可以直接使用外部函數的變量。
...     b()
... 
>>> 
>>> a(10)
10
10
>>> def a(x):
...     print(x)
...     def b():
...         print(x) #這里會報錯,因為在一旦在函數內部出現賦值操作,那么就會對外層的變量產生遮蔽效果,無論這賦值參數在本函數內部的位置是否在前,或者是在后,只要出現,就會出現遮蔽效果,此時如果在賦值操作之前使用該變量,就會出現未定義該變量的錯誤。 scala 的變量作用域效果與此類似。
...         x = 20
...         print(x)
...     b()
... 
>>> a(10)
10
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in a
  File "<stdin>", line 4, in b
UnboundLocalError: local variable 'x' referenced before assignment
>>> def a(x):
...     print(x)
...     def b():
...         nonlocal x #python3 的 nonlocal 語句可以內部函數直接使用外部函數
...         x = 20 
...         print(x)
...     b()
...     print(x)
... 
>>> a(10)
10
20
20


>>> def a(x):
...     print(locals())
...     def b():
...         print(x)
...         print(locals()) 
...     b()
... 
>>> a(1)
{'x': 1}
1
{'x': 1}
>>> def a(x):
...     print(locals())
...     def b():
...         print(locals())  #雖然賦值操作在本打印語句之后,但是仍然 可以看出,x 已經被遮蔽了
...         x = 20    #由於未使用nonlocal,此時的x 是一個和外部x 同名的全新的本地變量,所以,對他的任何修改,僅限於本函數內部,不會影響到外部。
...         print(locals())
...         print(x)
...     b()
...     print(x)  # 這是 遮蔽效果的證明   
... 
>>> a(10)
{'x': 10}
{}
{'x': 20}
20
10   
>>> 



閉包

def a():
    x = 20
    def b(y):
        return y+x # 注意閉包內部是直接使用 外層函數的變量,而沒有對外層函數變量進行賦值操作。
    return b(10)





def c():
    x = 20
    def b(y):
        x += 12  # 這里對x 進行了賦值操作。 x  = x+12 ,這會報錯,這其實已經不是閉包了,由於出現了賦值操作,所以,這里的 x 是一個和外層同名的變量,但是在本函數內部,本等式之前,並未進行 x 的宣告。所以會報錯
        return x +y
    return  b(10)

print(c())

貼一段scala 的閉包代碼:

object PackageStudy {
  def main(args: Array[String]): Unit = {
    def a(): Int = {
      var x = 10
      def b(y: Int): Int = {
        x += 13        // scala 可以做此操作,對捕獲的自由變量進行了修改,但是 python 不行,因為這個操作在python 內部是一個重新宣告x,重新賦值的操作,這會產生屏蔽效果,python 宣告變量 是“貼簽”操作。所以,python貌似是不支持對捕獲變量的修改。也就沒有 所謂在函數內部進行修改,會影響 外部的自由變量的值 這種情況的出現。
        x + y
      }
      def c(z: Int): Int = {
        x + z
      }
      println(x) //10
      println(b(10)) //33  第一次捕獲的自由變量的值是 x =10,所以此時的函數字面量中 x 綁定的是10
      println(x) //23 //在閉包內部對捕獲的自由變量的修改,也會影響到外面的自由變量。
      println(c(3)) //26 自由變量經過 b() 的修改,c(3)再次捕獲x時,x 已經是 23 了,此時的 函數字面量中綁定的x 是23
      0
    }
    println(a())
  }
}

python引用變量的順序: 當前作用域局部變量->外層作用域變量->當前模塊中的全局變量->python內置變量 。


免責聲明!

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



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