【目錄】
一、名稱空間
1. 內置名稱空間
2. 全域名稱空間
3. 區域名稱空間
二、作用域
1. 全域作用域與區域作用域
2. 作用域與名字查找的優先級
一、名稱空間
名稱空間(namespacs) :存放名字的地方,是對堆疊區的劃分,
有了名稱空間之后,就可以在堆疊區中存放相同的名字,詳細的名稱空間分為三種:內置名稱空間,全域名稱空間,區域名稱空間

L —— Local(function);函式內的名字空間
E —— Enclosing function locals;外部嵌套函式的名字空間(例如closure)
G —— Global(module);函式定義所在模塊(檔案)的名字空間
B —— Builtin(Python);Python內置模塊的名字空間
# builtin B —— Builtin(Python);Python內置模塊的名字空間
# global G —— Global(module);函式定義所在模塊(檔案)的名字空間
def f1():
# enclosing E —— Enclosing function locals;外部嵌套函式的名字空間(例如closure)
def f2():
# enclosing E —— Enclosing function locals;外部嵌套函式的名字空間(例如closure)
def f3():
# local L —— Local(function);函式內的名字空間
pass
1、內置名稱空間
# 存放的名字:存放的是python解釋器內置的名字
# 存活周期:python解釋器啟動則產生,python解釋器關閉則銷毀
# 在Python解釋器內: ''' >>> print <built-in function print> >>> input <built-in function input> '''
2、全域名稱空間
# 存放的名字:只要不是函式內定義、也不是內置的,剩下的都是全域名稱空間的名字
# 存活周期:python檔案執行則產生,python檔案運行完畢后銷毀
即 伴隨python檔案的開始執行/執行完畢而產生/回收,是第二個被加載的名稱空間,檔案執行程序中產生的名字都會存放于該名稱空間中,如下名字:
import sys #模塊名sys x=1 #變數名x if x == 1: y=2 #變數名y def foo(x): #函式名foo y=1 def bar(): pass Class Bar: #類名Bar pass
if c==0: x=2 #if 為頂級代碼,且不是函式,x為全域命名空間的名字 print(x)
3、區域名稱空間
# 存放的名字:在呼叫函式時,運行函式體代碼程序中產生的函式內的名字;
即 函式的形參、函式內定義的名字都會被存放于區域名稱空間中
# 存活周期:在呼叫函式時存活,函式呼叫完畢后則銷毀
def func(a,b): pass func(10,1) func(11,12) func(13,14) func(15,16)
def foo(x): y=3 #呼叫函式時,才會執行函式代碼,名字x和y都存放于該函式的區域名稱空間中
4、名稱空間加載順序
名稱空間的加載順序是:內置名稱空間->全域名稱空間->區域名稱空間
5、名稱空間銷毀順序
名稱空間銷毀順序與名稱空間的加載順序相反,即 區域名稱空間>全域名空間>內置名稱空間
6、名字的查找優先級
名稱空間只有優先級之分,本身并無嵌套關系,畫嵌套關系圖只是為了理解,
名稱空間的嵌套關系決定了名字的查找順序,而名稱空間的嵌套關系是以函式定義階段為準的,
函式的嵌套關系和名字查找順序是在函式定義階段就已經確定好了,
查找一個名字(按照定義階段的語法順序去找),必須從三個名稱空間之一找到,
由 當前所在的位置 向上一層一層查找——
內置名稱空間
全域名稱空間
區域名稱空間
(1)如果當前在區域名稱空間:
查找順序為:區域名稱空間->全域名稱空間->內置名稱空間->最后都沒有找到就會拋出例外
# input=333 #全域作用域的名字input def func(): input=444 # 區域名字input print(input) #在區域找input func() 輸出結果: 444
input=333 #全域作用域的名字input
def func():
# input=444 #區域沒有名字input,則找 全域作用域的名字input
print(input)
func()
輸出結果: 333
(2)如果當前在全域名稱空間
查找順序為:全域名稱空間->內置名稱空間->最后都沒有找到就會拋出例外
input=333 #全域變數
def func(): input=444 #在函式呼叫時產生區域作用域的名字input
func()
print(input) # 該陳述句不屬于函式體,屬于全域命名空間;函式呼叫時,對該陳述句沒有影響,所以直接執行列印功能,從全域命名空間開始找,即輸出全域變數 input=333 輸出結果: 333
# input=333
def func(): input=444 func()
print(input) # 該陳述句不屬于函式體,屬于全域命名空間;函式呼叫時,對該陳述句沒有影響,所以直接執行列印功能,從全域命名空間開始找,
全域空間沒有值,則在內置命名空間找,即 <built-in function input> 輸出結果: <built-in function input>
栗子1號:
def func(): print(x) x=111 # 全域變數 func()
栗子2號:名稱空間的"嵌套"關系是以函式定義階段為準,與呼叫位置無關
x=1 def func(): print(x) def foo(): x=222 func() foo()
栗子3號:函式嵌套定義——(函式嵌套,為了保證可讀性,最多三層嵌套)
python支持函式的嵌套定義,在內嵌的函式內查找名字時,會優先查找自己區域作用域的名字,
然后由內而外一層層查找外部嵌套函式定義的作用域,沒有找到,則查找全域作用域
先運行一個函式:
def f2(): input = 333 print(input) input = 222 # 是全域變數,函式f2呼叫時先在函式內的區域空間找值,有區域變數input = 333,故列印該值, f2() 輸出結果: 333
將上述函式作為子函式,函式的嵌套定義:
input=111 def f1(): def f2(): input=333 print(input) input=222 f2() f1() # f1嵌套子函式f2,子函式f2即為f1的函式體,所以函式呼叫執行程序同上 輸出結果: 333
input=111 def f1(): def f2(): # input=333 #將區域變數注釋,則f2執行時,在函式的區域空間內找不到值,會在其上一層的函式空間找,存在全域變數 input=222,執行f2,輸出結果 222 print(input) input=222 f2() f1() 輸出結果: 222
input=111 def f1(): def f2(): # input=333 print(input) # input=222 #若將f2的區域空間和最接近的全域空間的變數都去除,則函式f2執行時,會繼續往下一個全域空間找值,存在input=111,則輸出結果 111 f2() f1() 輸出結果: 111
栗子4號:先定義,后使用
x=111 def func(): print(x) x=222 func() # UnboundLocalError: local variable 'x' referenced before assignment # 即 變數x在使用前并未定義
修改一下:
x=111 def func(): x = 222 print(x) func() 輸出結果: 222
二、作用域
作用域,即 作用范圍,
1. 全域作用域與區域作用域
全域作用域:位于全域名稱空間、內建名稱空間中的名字屬于全域范圍
1、全域存活(除非被洗掉,否則在整個檔案執行程序中存活)
2、全域有效: 被所有函式共享(在任意位置都可以使用)
x=111 #全域變數,全域有效 def foo(): print(x,id(x)) def bar(): print(x,id(x)) foo() bar() print(x,id(x)) 輸出結果: 111 140730714743904 111 140730714743904 111 140730714743904
區域作用域: 位于區域名稱空間中的名字屬于區域范圍
1、臨時存活:即在函式呼叫時臨時生成,函式呼叫結束后就釋放
2、區域有效: 只能在函式內使用
x=100 #全域作用域的名字x def foo(): x=300 #區域作用域的名字x print(x) #在區域找x foo()#結果為300
2. 作用域與名字查找的優先級
(可先參照第一部分內容中的——6、名字查找的優先級)
先確定哪里有‘呼叫’操作,以此為起點,先不進行函式呼叫執行,而是以此為分水嶺,開始名字查找——
確定當前位置,從當前位置開始查找,若無,則繼續向上一層一層地往上查找,
總結如下:
(1)在區域作用域查找名字時,起始位置是區域作用域,所以先查找區域名稱空間,沒有找到,再去全域作用域查找:先查找全域名稱空間,沒有找到,再查找內置名稱空間,最后都沒有找到就會拋出例外
(2)在全域作用域查找名字時,起始位置便是全域作用域,所以先查找全域名稱空間,沒有找到,再查找內置名稱空間,最后都沒有找到就會拋出例外
(3)提示:可以呼叫內建函式locals()和globals()來分別查看區域作用域和全域作用域的名字,查看的結果都是字典格式,
在全域作用域查看到的locals()的結果等于globals()
(4)python支持函式的嵌套定義,在內嵌的函式內查找名字時,會優先查找自己區域作用域的名字,然后由內而外一層層查找外部嵌套函式定義的作用域,沒有找到,則查找全域作用域 (栗子4號)
(5)在函式內,無論嵌套多少層,都可以查看到全域作用域的名字,若要在函式內修改全域名稱空間中名字的值,當值為不可變型別時,則需要用到global關鍵字(栗子2號)
(6)對于嵌套多層的函式,使用nonlocal關鍵字可以將名字宣告為來自外部嵌套函式定義的作用域(非全域)(栗子5號)
(7)當實參的值為可變型別時,函式體內對該值的修改將直接反應到原值(栗子3號)
栗子1號:
x=111 def func(): x=222 func() print(x) # 處于全域空間,所以先從全域空間找,存在x=111,輸出結果 111
栗子2號:如果在區域想要修改全域的名字對應的值(不可變型別),需要用global,將該變數宣告為全域變數
x=111 def func(): global x # 宣告x這個名字是全域的名字,不要再造新的名字了 x=222 func() print(x) 輸出結果: 222
栗子3號:當實參的值為可變型別時,函式體內對該值的修改將直接反應到原值
l=[111,222] def func(): l.append(333) #l為可變型別,只是增減容器內的值,則沒有必要宣告為global func() print(l) 輸出結果: [111, 222, 333]
如果要將可變型別改為不可變型別,則要宣告global
l=[111,222]
def func():
global l
l=123 #l為可變型別,要修改它的值,則一定要宣告為global,才可修改
func()
print(l)
輸出結果:
[111, 222, 333]
栗子4號:函式嵌套定義
x=1
def outer():
x=2
def inner(): # 函式名inner屬于outer這一層作用域的名字
x=3
print('inner x:%s' %x)
inner()
print('outer x:%s' %x)
outer()
#結果為
inner x:3
outer x:2
栗子5號:nonlocal(了解): 修改函式外層函式包含的名字對應的值(不可變型別)
對于嵌套多層的函式,使用nonlocal關鍵字可以將名字宣告為來自外部嵌套函式定義的作用域(非全域)
nonlocal x會從當前函式的外層函式開始一層層去查找名字x,若是一直到最外層函式都找不到,則會拋出例外,
x=0 def f1(): x=11 def f2(): x=22 f2() print('f1內的x:',x) f1() #執行函式f1,先在函式體內的區域空間找值,存在x=11,則輸出;內置函式f2里的x=22,只對f2有用, 輸出結果: f1內的x: 11
x=0 def f1(): x=11 def f2(): nonlocal x #宣告 內置函式f2里的x=22,不是內置函式f2里的區域變數,則歸屬于外層函式f2,x=22會覆寫掉x=11,作為最后的結果輸出, x=22 f2() #呼叫f2(),修改f1作用域中名字x的值 print('f1內的x:',x) #在f1作用域查看x f1() 輸出結果: f1內的x: 22
參考:
https://zhuanlan.zhihu.com/p/108924801
https://www.cnblogs.com/linhaifeng/articles/7532497.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/170369.html
標籤:Python
上一篇:爬蟲之requests進階
