前言:在正式**前先丟擲幾個基本的概念,這幾個概念是官方文件中有宣告的概念,不過這些概念都包含有自己的一些理解,所以難免有些不太準確,請大家多多指正。
1.基本概念:
命名空間:名稱到物件值的乙個對映空間。
作用域:乙個**塊區域,此**塊區域可以訪問某些命名空間(訪問就是基於作用域)。
直接訪問:通過不加字首就可以訪問某個變數。(這就是我們討論作用域的目的)
屬性訪問:通過字首來訪問某個變數,如:copy.deepcopy()表示從copy的命名空間中找到變數deepcopy並使用。
請記住:作用域可以訪問多個命名空間(層層訪問),
某個命名空間是依賴某個作用域生成(不完全正確)。
2.變數搜尋路徑
我們執行**時就是要找到當前作用域可以直接訪問的命名空間,找到這個對應關係就可以確定變數的搜尋路徑,那麼變數對應的值就可以找到並使用,並且各個命名空間的變數不會互相影響。
那麼對於乙個**塊,它可以直接訪問(不是屬性訪問)到的命名空間有哪些呢?實際上這個訪問順序遵循乙個叫legb-rule的規則:
(1)先在區域性作用域對應的命名空間尋找,比如乙個函式內部的**塊就會首先在自己函式內部的命名空間尋找
(2)如果區域性作用域沒找到,它就會進入上一層作用域
(還不是全域性作用域)
對應的命名空間尋找,比如閉包結構就是這種情況
(3)接著會在全域性作用域尋找,即當前模組的命名空間
(4)最後會
__builtin__內建集合中尋找。
說到這裡其實作用域就已經說完了,如果在乙個**塊中,它呼叫乙個變數,然後挨個層級搜尋不就完了,找不到就報錯,找到就使用,這有什麼難的?如果真這麼簡單,那我怎麼還會寫這篇文章。
首先來看第乙個栗子:
栗子1:
a = "global var"
def f():
b = "local val"
def f2():
b = "abc"
print(a)
print(b)
f2()
print(b)
f()#輸出結果:
#global
#abc
#local val
解釋一下以上執行過程,在遇到print(a)時,此時是在f2這個函式的區域性作用域中,沒有搜尋到a,然後往上層的作用域也就是f函式的作用域尋找,也沒有找到,接著到達全域性作用域尋找,找到了a = "global var"。在遇到print(b)時此時也在f2這個函式的作用域中,找到了b = "abc"。接著在f2()執行完後,執行第二個print(b)它首先在f函式的作用域中尋找(閉包的空間),找到了b = "local val"。是不是很簡單?其實還沒完,如果我在這樣修改呢?
栗子2:
a = "global var"
def f():
def f2():
print(a)
a = "local var"
f2()
f()#輸出:unboundlocalerror: local variable 'a' referenced before assignment
這是為什麼呢?這是因為雖然python是一門動態語言但是為了提高效率,python會把一些工作在編譯時就完成,而不會等到動態執行時才完成。比如在建立區域性變數時,還沒有執行某**塊的語句,區域性命名空間就建立好了。在這個栗子中,當**執行剛進入f2時在f2內就建立了乙個命名空間將變數a新增進去,還沒實際賦值(這裡我不了解底層的實現原理),但是執行print(a)時卻找不到a的實際值,因為還沒有實際執行a = 「local var"。
記住以上兩個栗子你就大概了解了命名空間和作用域的聯絡和作用。
有的童鞋可能會想,既然可以訪問各個命名空間,那麼是否可以修改命名空間的變數值呢?答案是肯定的,這就要借助global和nonlocal語句。
global語句,直接操作全域性命名空間的變數。
栗子3:
a = 11
def f():
def f2():
global a #在此作用域內可以操作全域性作用域的變數
a = 22
print(a)
f2()
f()print(a)
#輸出#22
#22
nonlocal語句,直接操作上層命名空間和本層命名空間的變數。
栗子4:
b = 11
def f():
b = 22
def f2():
nonlocal b
b = 33
print(b)
f2()
print(b)
f()print(b)
#輸出#33
#33 #證明修改b有效
#11 #沒有修改全域性命名空間的b
是不是感覺對命名空間和作用域瞭如指掌,我們再來看看類空間。
粒子5:
class student(object):
a = 123
print(a)
def func():
print(a)
student.func()
#輸出:nameerror: name 'a' is not defined
這說明func函式根本就找不到a變數,但是不是按照legb規則不是能找到的嗎?這裡類空間有點特殊,類空間建立了乙個命名空間但是卻沒有作用域,這就決定了誰都無法直接訪問到它,除了在它的空間。我們把**這樣修改一下
栗子6:
class student(object):
a = 123
print(a)
def func():
print(student.a)
student.func()
#輸出:123
這樣就可以訪問了,student.a表示訪問student這個命名空間的a變數。這裡說明類和函式的執行機制不相同,函式被建立後不會立刻被執行但是會建立對應的命名空間和作用域,但是類在建立後會被立刻執行,因為類是以後例項被建立的基礎,這樣可以提高效率和降低邏輯複雜度。
栗子7:
class student(object):
a = 123
print(a)
print(student.a)
#輸出:nameerror: name 'student' is not defined
因為student類還沒有被建立成功,只有把類的**塊執行完成後,類才算建立成功,後面的例項才能依次建立。
栗子8:
a = 1
class student(object):
print(a)
a = 123
#輸出:1
沒有報錯,說明類空間的命名空間是動態建立的。
總結:
命名空間:名稱到物件值的乙個對映空間。
作用域:乙個**塊區域,此**塊區域可以訪問某些命名空間(訪問就是基於作用域)。
作用域可以訪問多個命名空間(層層訪問),某個命名空間是依賴某個作用域生成(類空間是不是)。
命名空間的搜尋遵循legb-rule,依次搜尋。
作用域與命名空間的生成是在**動態執行之前,不是在執行的過程中(如栗子2),但是類空間例外(如栗子8)。
類空間只會生成命名空間,不會生成作用域,因此在除在類空間以外的訪問都需要通過類名的屬性訪問。
Python作用域 全域性作用域 區域性作用域
在python中,每個函式都會建立乙個作用域。pythonistas也可能稱函式擁有它們自己的命名空間 namespace 這意味著當在函式體裡遇到變數名 時,python首先在該函式的命名空間中查詢,python包含了一些讓我們檢視命名空間的函式。讓我們寫乙個簡單的函式來探查一下local和glo...
Python的作用域
在這篇文章裡,我們來關注作用域在python被誤用的地方。通常,當我們定義了乙個全域性變數 好吧,我這樣說是因為講解的需要 全域性變數是不好的 我們用乙個函式訪問它們是能被python理解的 bar 42 def foo print bar在這裡,我們在foo函式裡使用了全域性變數bar,然後它也如...
python變數作用域
變數作用域 scope 在python中是乙個容易掉坑的地方。什麼是作用域 作用域簡單說就是乙個變數的命名空間。中變數被賦值的位置,就決定了哪些範圍的物件可以訪問這個變數,這個範圍就是命名空間。python賦值時生成了變數名,當然作用域也包括在內。python的作用域一共有4中,分別是 l loca...