目錄
一、函數的命名空間和作用域
1、函數的命名空間
什麼是命名空間?
函數命名空間三大類
三種命名空間之間的加載與取值順序
2、函數的作用域
globals和locals方法
global關鍵字
二、函數的嵌套和作用域鏈
函數的嵌套調用
函數的嵌套定義
函數的作用域鏈
nonlocal關鍵字
global關鍵字
三、函數名的本質
函數名就是內存地址
函數名可以被賦值
函數名可以作為容器類型的元素
函數名可以作為函數的返回值
函數名可以作為函數的參數
思考
四、閉包
閉包函數的概念
閉包函數的判斷方法
閉包嵌套
小結
假如有一串代碼,觀察其輸出結果:
def f(): a = 1 return a print(a) 輸出結果: Traceback (most recent call last): File "E:/python代碼/11/文件一.py", line 4, in <module> print(a) NameError: name 'a' is not defined
報錯了!錯誤是“name 'a' is not defined”。變量a沒有被定義。。。為啥?我明明定義了a=1呀!
那我們就要了解一下Python代碼運行時遇到函數時怎麼做的:
首先從python解釋器開始執行之後,就在內存中開辟了一個空間每當遇到一個變量的時候,就把變量名和值之間的對應關系記錄下來。但是當遇到函數定義的時候解釋器只是象征性的將函數名讀入內存,表示知道這個函數的存在了,至於函數內部的變量和邏輯解釋器根本不關心。等執行到函數調用的時候,python解釋器會再開辟一塊內存來存儲這個函數裡的內容,這個時候,才關注函數裡面有哪些變量,而函數中的變量會存儲在新開辟出來的內存中。函數中的變量只能在函數的內部使用,並且會隨著函數執行完畢,這塊內存中的所有內容也會被清空。
我們給這個“存放名字與值的關系”的空間起了一個名字——叫做命名空間
代碼在最開始創建的存儲“變量名與值的關系”的空間叫做全局命名空間,在函數內部運行中開辟的臨時的空間叫做局部命名空間
函數命名空間一共分為三大類
1、內置命名空間 —— python解釋器 # 就是python解釋器一啟動就可以使用的名字存儲在內置命名空間中 # 內置的名字在啟動解釋器的時候被加載進內存裡 2、全局命名空間 —— 我們寫的代碼但不是函數中的代碼 # 是在程序從上到下被執行的過程中依次加載進內存的 # 放置了我們設置的所有變量名和函數名 3、局部命名空間 —— 函數 # 就是函數內部定義的名字 # 當調用函數的時候 才會產生這個名稱空間 隨著函數執行的結束 這個命名空間就又消失了 #在局部:可以使用全局、內置命名空間中的名字 #在全局:可以使用內置命名空間中的名字,但是不能用局部中使用 #在內置:不能使用局部和全局的名字的內置命名空間:內置命名空間中存放了Python解釋器為我們提供的名字(函數)我們不需要定義,都是我們熟悉的打開解釋器就能夠直接使用如:input、print、str、set……
加載順序:內置命名空間(程序運行前加載 > 全局命名空間(程序運行中:從上到下加載) > 局部命名空間(程序運行中:調用時才加載)
在局部調用時:局部命名空間 > 全局命名空間 > 內置命名空間
在全局調用時:全局命名空間 > 內置命名空間
例子:
a = 10 def f(): a = 1 print(a) f() print(a) 輸出結果: 1 10
作用域就是作用范圍,按照生效范圍可以分為全局作用域和局部作用域。
全局作用域:包含內置名稱空間、全局名稱空間,在整個文件的任意位置都能被引用、全局有效
局部作用域:局部名稱空間,只能在局部范圍內生效
locals():
函數會以字典類型返回當前位置的全部局部變量。globals():
函數會以字典類型返回當前位置的全部全局變量。def func(): a = 1 print(locals()) print(globals()) print('========================分割線==========================') func() print(locals()) print(globals())
輸出結果:
1、global是Python中的全局變量關鍵字。
2、變量分為局部變量與全局變量,局部變量又可稱之為內部變量。
3、由某對象或某個函數所創建的變量通常都是局部變量,只能被內部引用,而無法被其它對象或函數引用。
4、全局變量既可以是某對象函數創建,也可以是在本程序任何地方創建。全局變量是可以被本程序所有對象或函數引用。
5、global關鍵字的作用是可以使得一個局部變量為全局變量例子:
在my函數中,在 x 前面加 global,my函數將 x 賦為8,此時全局變量中的 x 值改變。需要注意的是 global 需要在函數內部聲明,若在函數外聲明,則函數依然無法操作 x 。
x = 4 def my(): global x x = 8 print("x = ", x) print("x = ", x) my() print("x = ", x) 輸出結果是: x = 4 x = 8 x = 8
def max2(x,y):
m = x if x>y else y
return m
def max4(a,b,c,d):
res1 = max2(a,b)
res2 = max2(res1,c)
res3 = max2(res2,d)
return res3
ret = max4(1,2,4,3)
print(ret)
輸出結果:
4
def f1():
print("in f1")
def f2():
print("in f2")
f2()
f1()
輸出結果:
in f1
in f2
def f1():
def f2():
def f3():
print("in f3")
print("in f2")
f3()
print("in f1")
f2()
f1()
輸出結果:
in f1
in f2
in f3
a = 1
def outer():
a = 5
def inner():
a = 2
def inner2():
nonlocal a
a += 1
print('inner2',a)
inner2()
print('##a##:',a)
inner()
print('**a**:',a)
outer()
print('全局:',a)
輸出結果:
inner2 3
##a##: 3
**a**: 5
全局: 1
#nonlocal 只能用於局部變量,找上層中離當前函數最近一層的局部變量且外部必須有這個變量 #聲明了nonlocal的內部函數的變量修改會影響到離當前函數最近一層的局部變量 #對全局無效,在內部函數聲明nonlocal變量之前不能再出現同名變量 #對局部也只是對最近一層有影響def f1(): a = 1 def f2(): nonlocal a a = 2 f2() print('a in f1 : ',a) f1() 輸出結果: a in f1 : 2
# 對於不可變數據類型 在局部可是查看全局作用域中的變量 # 但是不能直接修改 # 如果想要修改,需要在程序的一開始添加global聲明 # 如果在一個局部(函數)內聲明了一個global變量,那麼這個變量在局部的所有操作將對全局的變量有效
函數名就是內存地址
函數名可以被賦值
函數名可以作為容器類型的元素
函數名可以作為函數的返回值
函數名可以作為函數的參數
def func(): print(123) func() print(func) # 函數名就是內存地址 # 函數名可以被賦值 func2 = func func2() #函數名可以作為容器類型的元素 l = [func,func2] for i in l: i() def func(): print(123) def wahaha(f): f() return f #函數名可以作為函數的返回值 qqxing = wahaha(func) #函數名可以作為函數的參數 qqxing() 輸出結果: 123 <function func at 0x000001ADF9946280> 123 123 123 123 123
如果我自己定義了一個input函數(作用:調用該函數就打印'在下周周ovo'),會不會與內置的input函數有沖突呢?
def input(a): print('在下周周ovo')
那麼接下來的代碼怎麼運行呢?
def input(a): print('在下周周ovo') def func(): input('請輸入') print(input) func()
答案:
內部函數包含對外部作用域而非全劇作用域名字的引用,該內部函數稱為閉包函數
#函數內部定義的函數稱為內部函數由於有了作用域的關系,我們就不能拿到函數內部的變量和函數了。如果我們就是想拿怎麼辦呢?返回呀!
如果函數內的變量我們要想在函數外部用,可以直接返回這個變量,那麼如果我們想在函數外部調用函數內部的函數呢?那就直接將函數名字作為返回值就好
def outer(): a = 1 def inner(): print(a) #內部函數調用了外部變量a return inner inn = outer() inn() 輸出結果: 1
判斷閉包函數的方法__closure__
當運行後,如果有cell的話,就表示是閉包函數。如果沒有就不是。
#輸出的__closure__有cell元素 :是閉包函數 def func(): name = 'eva' def inner(): print(name) print(inner.__closure__) return inner f = func() f() #輸出的__closure__為None :不是閉包函數 name = 'egon' def func2(): def inner(): print(name) print(inner.__closure__) return inner f2 = func2() f2() 輸出結果: (<cell at 0x000001E935CB0FA0: str object at 0x000001E935CC2CB0>,) eva None egon
顧名思義是兩個或以上的閉包函數嵌套在一起
def wrapper(): money = 10 def func(): name = 'zhou' def inner(): print(name,money) #引用了func()函數中name變量引用了wrapper()函數中money變量 return inner return func f = wrapper() i = f() i() 輸出結果: zhuo 10
#func(一個函數名) --->>對應函數的內存地址 #函數名()---函數調用 #函數的內存地址----()函數的調用 # 作用域兩種 # 全局作用域 —— 作用在全局 —— 內置和全局名字空間中的名字都屬於全局作用域 ——globals() # 局部作用域 —— 作用在局部 —— 函數(局部名字空間中的名字屬於局部作用域) ——#locals()globals() : 永遠打印全局的名字 #locals() : 輸出什麼 根據locals所在位置 #在代碼中要盡量少定義全局變量,多使用返回值和接收返回值 #函數的嵌套: 嵌套調用 嵌套定義:定義在內部的函數無法直接在全局被調用 #函數名的本質: 就是一個變量,保存了函數所在的內存地址 #閉包: 內部函數包含對外部作用域而非全劇作用域名字的引用,該內部函數稱為閉包函數
最後感謝各位友友能夠看到這裡:
在魯迅一篇未發表的文章中說過:“代碼看懂了不是懂一定要自己實際操作哇這樣才能更好的理解和吸收。”
最後來一句:一個人可以在任何他懷有無限熱忱的事情上成功,讓我們一起進步吧