1. 程式人生 > 實用技巧 >Python名稱空間與作用域

Python名稱空間與作用域

名稱空間:

  • 存放名字與值的關係的空間

  • 在python直譯器開始執行, 就會在記憶體中開闢一個空間, 每當遇到一個變量的時候, 就把變量名和值之間的關係記錄下來,

  • 當遇到函式定義的時候, 直譯器只是把函式名讀入記憶體, 並檢查語法是否正確,表示這個函式存在了, 至於函式內部的變量和邏輯, 直譯器是不關心的。

  • 只有當函式被呼叫和訪問的時候, 直譯器才會根據函式內部宣告的變量來進行開闢變量的內部空間.

內建名稱空間:

  • 存放Python直譯器內建的名字,如input,print,list等
  • 存活週期:Python直譯器啟動則產生,Python直譯器關閉則銷燬

全域性名稱空間:

  • 執行程式碼伊始所產生的名字,或者說不是函式內定義、也不是內建的剩下的都是全域性名稱空間
  • 存活週期:Python檔案執行產生,檔案執行結束銷燬

臨時名稱空間:

  • 在函式體的執行中開闢的臨時的空間,也叫做區域性名稱空間,同一個函式呼叫多次會產生多個區域性名稱空間。
  • 存活週期:函式體執行時產生,函式執行結束,這個空間也會清空

載入到記憶體的順序:

  • 1.內建名稱空間

  • 2.全域性名稱空間

  • 3.臨時名稱空間

取值順序:(就近原則:LEGB原則)單向不可逆

  • (從區域性開始找時)區域性名稱空間 --> 全域性名稱空間 --> 內建名稱空間
input = 333
print(input) # 此時是從全域性開始取值,input()函式則不會被查詢到
# 333

LEGB原則:

  • local本地 --> enclosed巢狀函式的外層函式內部 --> global全域性 --> buildin內建
def func():
    print(x)

x = 111
func()		# 區域性沒有則在全域性查詢,在函式執行前x被賦值,所以並不會報錯
# 111


x = 1
def func():
    print(x)
    
def foo():
    x = 222
    func()

foo()		# 名稱空間的'巢狀'關係是以函式定義階段為準,與呼叫位置無關
#  1		


input = 111
def f1():
    def f2():
        input = 333
        print(input)
    input = 222
    f2()
f1()			# 名稱空間的'巢狀'關係是以函式定義階段為準,與呼叫位置無關
#  333 

注:名稱空間實際上是相互獨立的一個個空間,並沒有包含關係,巢狀關係僅為了幫助理解

作用域:

  • 變數的生效範圍,分為全域性作用域和區域性作用域

全域性作用域:

  • 內建名稱空間,全域性名稱空間,在整個檔案的任何位置都可以執行(遵循從上到下逐行執行)

區域性作用域:

  • 區域性名稱空間,在函式內部可以使用。
  • 區域性作用域可以引用全域性作用域的變數,但不可改變:
    • 因為當Python讀取到區域性作用域時,發現對變數進行了更改,
      直譯器會認為這個變數在區域性已經定義,就會從區域性名稱空間尋找這個變數,
      然而區域性空間並沒有定義這個變數,所以報錯。
count = 1
def func():
	count = 100  # 這是在區域性名稱空間重新建立變數count,並非修改了全域性名稱空間的count
	print(count)
func()
# 100

count = 1
def func():
    count += 1		# 不可更改
    print(count)
func()
#  local variable 'count' referenced before assignment

高階函式:

  • 函式內巢狀函式。

作用域相關的內建函式:

  • globals():返回的是字典,裡面的鍵值對為全域性作用域的所有內容
print(globals())
  • locals():返回的是字典,裡面的鍵值對為當前作用域的所有內容
print(globals())

函式中變數引用的坑:

  • 在函式中,如果你定義了一個變數,但是在定義之前引用了此變數,那麼直譯器會認為,語法錯誤。應該在引用之前先定義。
count = 1
def func():
    print(count)     
    count = 2
func() 
#  local variable 'count' referenced before assignment

關鍵字:

  • global:
    • 在區域性作用域宣告一個全域性變數。
    • 如果區域性想要修改全域性的名字對應的值(不可變資料型別),也需要用global。

name = '杜甫'
def func():
    global name    	 # 變數賦值前先宣告name是全域性名稱,不要再造新名稱
    name = '李白'		
func()             	 # 函式呼叫後,函式內宣告的全域性變數才生效
print(name)
#  李白

  • 如果區域性想要修改全域性的名字對應的值(可變型別),則不需要global:
l1 = [111,222]
def func():
    l1.append(333)

func()
print(l1)
# [111, 222, 333]
  • nonlocal:
    • 在巢狀函式中,內層函式對上一層函式內名稱的修改(不可變型別),需要nonlocal
    • nonlocal的使用必須是內層函式對非全域性的外層函式,如果外層函式沒有找到被宣告的名稱,則會報錯
x = 0
def f1():
    x = 11		# 如果f1內的x註釋掉,nonlocal則報錯
    def f2():
        nonlocal x
        x = 22
    f2()
    print('f1內的x:',x)
f1()
# f1內的x: 22


x = 0
def f1():
    x = [11,]		
    def f2():
        x.append(22)		# 如果是可變型別則不需要nonlocal,可直接修改
    f2()
    print('f1內的x:',x)
f1()
# f1內的x: [11, 22]