最近看 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內置變量 。