本文和大家分享的主要是python
的標(biāo)準(zhǔn)庫Scope相關(guān)內(nèi)容,一起來看看吧,希望對大家
學(xué)習(xí)python有所幫助。
1
模塊簡介
你一定在很多計算機科學(xué)課程上聽說過作用域。它很重要,如果你不理解它的工作原理,那么就會出現(xiàn)一些令人困惑的錯誤。作用域最基本的功能就是告訴編譯器一個變量什么時候是可見的。也就是說,作用域定義了你使用變量的時間和范圍。當(dāng)你嘗試使用一些不在當(dāng)前作用域的變量時,你就會得到NameError
。
Python
有三類作用域:
·
局部作用域;
·
全局作用域;
·
非局部作用域(
Python 3
中新增);
2
模塊使用
2.1
局部作用域
局部作用域是Python
中使用最多的作用域。當(dāng)你在一段代碼塊中創(chuàng)建一個變量,它將會在最近的作用域中使用。所有的作用域組成的集合就是代碼塊環(huán)境。也就是說,默認(rèn)是在局部作用域中處理所有的任務(wù)。如果你想要不同的作用域,那么你需要將變量設(shè)置為全局作用域或非局部作用域。
現(xiàn)在,我們使用Python
的解釋器創(chuàng)建一個簡單的例子,來展示局部作用域任務(wù)。
>>> x = 10>>> def my_func(a,b):... print(x)... print(z)... >>> my_func(1,2)10
Traceback (most recent call last):
File "", line 1, in
File "", line 3, in my_func
NameError: global name 'z' is not defined
這里,我們創(chuàng)建了變量x
以及一個入?yún)閮蓚€參數(shù)的簡單函數(shù)。它將會打印
x
和
z
。請記住,我們還沒有定義
z
,所以當(dāng)我們調(diào)用這個函數(shù)時,我們將會獲得
NameError
。這是因為
z
還沒有定義或者它在作用域外部。如果你在調(diào)用函數(shù)之前定義
z
,那么就會發(fā)現(xiàn)
z
,你就不會獲得
NameError
。
如果你嘗試訪問函數(shù)內(nèi)部的變量,那么你也會獲得NameError
。
>>> def my_func(a,b):... i = 2... print(x)... >>> if __name__ == "__main__":... x = 10... my_func(1,2)... print(i)... 10
Traceback (most recent call last):
File "", line 4, in
NameError: name 'i' is not defined
變量i
僅僅在函數(shù)內(nèi)部定義,所以當(dāng)你運行這段代碼時,你將會得到
NameError
。
我們稍微修改一下上面的代碼,將下面的代碼存儲到文件中,并運行。
def my_func(a,b):
x = 5
print(x)
if __name__ == "__main__":
x = 10
my_func(1,2)
print(x)
你認(rèn)為將會發(fā)生什么?10
打印兩次?實際上并不是。原因就是我們現(xiàn)在有兩個
x
變量。
my_func
函數(shù)中的變量
x
是局部函數(shù)作用域,它將會覆蓋函數(shù)外部的變量
x
。當(dāng)我們調(diào)用
my_func
函數(shù)時,我們打印
5
而非
10
。當(dāng)函數(shù)返回時,
my_func
函數(shù)中的變量
x
會被回收,函數(shù)外的變量
x
將會起作用,這就是為什么最后一行語句打印出
10
。
如果你想了解具體的技巧,你可以在函數(shù)中的賦值語句前打印x
,如下所示,
>>> def my_func(a,b):... print(x)... x = 5... print(x)... >>> if __name__ == "__main__":... x = 10... my_func(1,2)... print(x)
當(dāng)你運行這段代碼時,你將會得到如下的異常,
Traceback (most recent call last):
File "", line 3, in <module>
File "", line 2, in my_func
UnboundLocalError: local variable 'x' referenced before assignment
這個異常發(fā)生,是因為Python
提示你在
my_func
函數(shù)中后賦值給
x
,由于
x
還沒有定義,因此它拋出異常。
2.2
全局作用域
Python
包括
global
語句。它是
Python
的一個關(guān)鍵字。
global
語句定義了這個變量可以在隨后的代碼塊中作為變量使用。雖然你可以在聲明全局之前創(chuàng)建一個名稱,但這是非常不鼓勵的。讓我們嘗試使用
global
來修復(fù)上一個例子拋出的異常:
def my_func(a,b):
global x
print(x)
x = 5
print(x)
if __name__ == "__main__":
x = 10
my_func(1,2)
print(x)
這段代碼的輸出是,
通過將x
定義為全局變量,我們告訴
Pyton
在函數(shù)中第一個打印函數(shù)中首先使用第一個定義的
x
。然后我們給
x
賦予新值
5
,在退出函數(shù)前再次打印。你將會注意到現(xiàn)在
x
是全局變量,當(dāng)我們到達(dá)代碼塊的最后一個輸出語句時,
x
依然是
5
。
讓我們混合使用global
和
local
來做一些有意思的事情,
def my_func(a,b):
global c
b,a = a,b
d = 'Mike'
print(a,b,c,d)
a , b , c , d = 1 , 2 , 'c is global' , 4
my_func(a,b)
print(a , b , c , d)
在這里,我們將變量c
設(shè)置為全局變量。這個將會導(dǎo)致在函數(shù)內(nèi)部和外部,
c
都會輸出相同的值。我們在函數(shù)內(nèi)部交換變量
a
和
b
,可以顯示出我們在函數(shù)內(nèi)部對其進(jìn)行了交換,但是在函數(shù)外部,并沒有修改二者。這也顯示出變量
a
和
b
并不是全局變量,你應(yīng)該可以看到如下的輸出結(jié)果:
(2, 1, 'c is global', 'Mike')
(1, 2, 'c is global', 4)
我在此想提醒你不要在函數(shù)內(nèi)部修改全局變量。這在Python
社區(qū)中是一個不好的例子,它也會導(dǎo)致調(diào)試更加困難。
現(xiàn)在,我們已經(jīng)理解了局部和全局變量,下面我們將要了解非局部變量。
2.3
非局部作用域
Python 3
新增了一個關(guān)鍵詞
--nonlocal
。關(guān)鍵詞
nonlocal
增加了一個作用域用于覆蓋內(nèi)部作用域。你可以閱讀
PEP 3104
。下面一段代碼可以很好的解釋非局部作用域。最常見的例子就是創(chuàng)建一個自增函數(shù),
def counter():
num = 0
def incrementer():
num += 1
return num
return incrementer
如果你運行這段代碼,你將會得到UnboundLocalError
這個錯誤,因為變量
num
在內(nèi)部函數(shù)中在賦值之前引用。讓我們增加局部作用域,
>>> def counter():... num = 0... def incrementer():... nonlocal num... num += 1... return num... return incrementer... >>> >>> c = counter()>>> c
<function counter..incrementer at 0x7f67735ffea0>>>> c()1>>> c()2>>> c()3
現(xiàn)在我們定義的自增函數(shù)已按照我們期望開始工作。這種類型的函數(shù)被稱為closure
(閉包)。閉包就是一個將非局部變量封裝起來的代碼塊。閉包背后的思想就是你可以在函數(shù)外部引用這些變量。
nonlocal
允許你將變量分配到作用域外,但不是全局作用域。你不能在
counter
函數(shù)中使用
nonlocal
,因為它嘗試著將其分配到全局作用域。你可以嘗試一下,你將會得到
SyntaxError
。所以你必須在嵌套函數(shù)中使用
nonlocal
。
2.4
總結(jié)
在本文中,我們了解了通過Python
關(guān)鍵詞
global
和
nonlocal
來修改變量的引用方式。我們學(xué)習(xí)了在哪里使用以及為什么。我們也學(xué)習(xí)了局部作用域。
來源:
博客園