1. 程式人生 > 實用技巧 >python函式----名稱空間和作用域

python函式----名稱空間和作用域

一 名稱空間

名稱空間即存放名字與物件對映/繫結關係的地方。
對於x=3,Python會申請記憶體空間存放物件3,然後將名字x與3的繫結關係存放於名稱空間中,del x表示清除該繫結關係。
​在程式執行期間最多會存在三種名稱空間

1.1 內建名稱空間

伴隨python直譯器的啟動/關閉而產生/回收,因而是第一個被載入的名稱空間,用來存放一些內建的名字,比如內建函式名
>>> max
<built-in function max> #built-in內建

1.2 全域性名稱空間

伴隨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

1.3 區域性名稱空間

伴隨函式的呼叫/結束而臨時產生/回收,函式的形參、函式內定義的名字都會被存放於該名稱空間中
def foo(x):
    y=3 #呼叫函式時,才會執行函式程式碼,名字x和y都存放於該函式的區域性名稱空間中
名稱空間的載入順序是:內建名稱空間->全域性名稱空間->區域性名稱空間,而查詢一個名字,必須從三個名稱空間之一找到,查詢順序為:區域性名稱空間->全域性名稱空間->內建名稱空間。

二 作用域

2.1 全域性作用域與區域性作用域

按照名字作用範圍的不同可以將三個名稱空間劃分為兩個區域:
全域性作用域:位於全域性名稱空間、內建名稱空間中的名字屬於全域性範圍,該範圍內的名字全域性存活(除非被刪除,否則在整個檔案執行過程中存活)、全域性有效(在任意位置都可以使用);
區域性作用域:位於區域性名稱空間中的名字屬於區域性範圍。該範圍內的名字臨時存活(即在函式呼叫時臨時生成,函式呼叫結束後就釋放)、區域性有效(只能在函式內使用)。

2.2 作用域與名字查詢的優先順序

​ 在區域性作用域查詢名字時,起始位置是區域性作用域,所以先查詢區域性名稱空間,沒有找到,再去全域性作用域查詢:先查詢全域性名稱空間,沒有找到,再查詢內建名稱空間,最後都沒有找到就會丟擲異常
x=100 #全域性作用域的名字x
def foo():
    x=300 #區域性作用域的名字x
    print(x) #在區域性找x
foo()#結果為300
在全域性作用域查詢名字時,起始位置便是全域性作用域,所以先查詢全域性名稱空間,沒有找到,再查詢內建名稱空間,最後都沒有找到就會丟擲異常
x=100
def foo():
    x=300 #在函式呼叫時產生區域性作用域的名字x
foo()
print(x) #在全域性找x,結果為100
提示:可以呼叫內建函式locals()和globals()來分別檢視區域性作用域和全域性作用域的名字,檢視的結果都是字典格式。在全域性作用域檢視到的locals()的結果等於globals()
Python支援函式的巢狀定義,在內嵌的函式內查詢名字時,會優先查詢自己區域性作用域的名字,然後由內而外一層層查詢外部巢狀函式定義的作用域,沒有找到,則查詢全域性作用域
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
在函式內,無論巢狀多少層,都可以檢視到全域性作用域的名字,若要在函式內修改全域性名稱空間中名字的值,當值為不可變型別時,則需要用到global關鍵字
x=1
def foo():
    global x #宣告x為全域性名稱空間的名字
    x=2
foo()
print(x) #結果為2
當實參的值為可變型別時,函式體內對該值的修改將直接反應到原值,
num_list=[1,2,3]
def foo(nums):
    nums.append(5)
foo(num_list)
print(num_list)
[1, 2, 3, 5]
對於巢狀多層的函式,使用nonlocal關鍵字可以將名字宣告為來自外部巢狀函式定義的作用域(非全域性)
def  f1():
    x=2
    def f2():
        nonlocal x
        x=3
    f2() #呼叫f2(),修改f1作用域中名字x的值
    print(x) #在f1作用域檢視x
f1()
3
nonlocal x會從當前函式的外層函式開始一層層去查詢名字x,若是一直到最外層函式都找不到,則會丟擲異常