python作用域初探

2021-07-25 03:42:43 字數 4076 閱讀 3932

前言:在正式**前先丟擲幾個基本的概念,這幾個概念是官方文件中有宣告的概念,不過這些概念都包含有自己的一些理解,所以難免有些不太準確,請大家多多指正。

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...