標籤:範圍鏈 報錯 返回頂部 函數 fc7 www back strong func
楔子
假如有一個函數,實現返回兩個數中的較大值:
def my_max(x,y): m = x if x>y else y return m
bigger = my_max(10,20)
print(bigger)
之前是不是我告訴你們要把結果return回來你們就照做了?可是你們有沒有想過,我們為什麼要把結果返回?如果我們不返回m,直接在程式中列印,行不行?
來看結果:
>>> def my_max(x,y):... m = x if x>y else y... >>> my_max(10,20)>>> print(m)Traceback (most recent call last): File "<stdin>", line 1, in <module>NameError: name ‘m‘ is not defined
報錯了!錯誤是“name ‘m‘ is not defined”。變數m沒有被定義。。。為啥?我明明定義了呀!
在這裡我們首先回憶一下python代碼啟動並執行時候遇到函數是怎麼做的。
從python解譯器開始執行之後,就在記憶體中開闢了一個空間
每當遇到一個變數的時候,就把變數名和值之間的對應關聯性記錄下來。
但是當遇到函數定義的時候解譯器只是象徵性的將函數名讀入記憶體,表示知道這個函數的存在了,至於函數內部的變數和邏輯解譯器根本不關心。
等執行到函數調用的時候,python解譯器會再開闢一塊記憶體來儲存這個函數裡的內容,這個時候,才關注函數裡面有哪些變數,而函數中的變數會儲存在新開闢出來的記憶體中。函數中的變數只能在函數的內部使用,並且會隨著函數執行完畢,這塊記憶體中的所有內容也會被清空。
我們給這個“存放名字與值的關係”的空間起了一個名字——叫做命名空間
代碼在運行伊始,建立的儲存“變數名與值的關係”的空間叫做全域命名空間,在函數的運行中開闢的臨時的空間叫做局部命名空間
返回頂部
命名空間和範圍
命名空間的本質:存放名字與值的綁定關係
python之禪
在python之禪中提到過:命名空間是一種絕妙的理念,讓我們盡情的使用發揮吧!
命名空間一共分為三種:
全域命名空間
局部命名空間
內建命名空間
*內建命名空間中存放了python解譯器為我們提供的名字:input,print,str,list,tuple...它們都是我們熟悉的,拿過來就可以用的方法。
三種命名空間之間的載入與取值順序:
載入順序:內建命名空間(程式運行前載入)->全域命名空間(程式運行中:從上到下載入)->局部命名空間(程式運行中:調用時才載入)
取值:
在局部調用:局部命名空間->全域命名空間->內建命名空間
x = 1def f(x): print(x)print(10)
在全域調用:全域命名空間->內建命名空間
x = 1def f(x): print(x)f(10)print(x)
print(max)
範圍
範圍就是作用範圍,按照生效範圍可以分為全域範圍和局部範圍。
全域範圍:包含內建名稱空間、全域名稱空間,在整個檔案的任意位置都能被引用、全域有效
局部範圍:局部名稱空間,只能在局部範圍內生效
globals和locals方法
print(globals())print(locals())
def func(): a = 12 b = 20 print(locals()) print(globals())func()
global關鍵字
a = 10def func(): global a a = 20print(a)func()print(a)
返回頂部
函數的嵌套和範圍鏈
函數的嵌套調用
def max2(x,y): m = x if x>y else y return mdef max4(a,b,c,d): res1 = max2(a,b) res2 = max2(res1,c) res3 = max2(res2,d) return res3# max4(23,-7,31,11)
函數的嵌套定義
def f1(): print("in f1") def f2(): print("in f2") f2()f1()
def f1(): def f2(): def f3(): print("in f3") print("in f2") f3() print("in f1") f2() f1()
函數的範圍鏈
def f1(): a = 1 def f2(): print(a) f2()f1()
def f1(): a = 1 def f2(): def f3(): print(a) f3() f2()f1()
def f1(): a = 1 def f2(): a = 2 f2() print(‘a in f1 : ‘,a)f1()
nonlocal關鍵字
# 1.外部必須有這個變數
# 2.在內建函式聲明nonlocal變數之前不能再出現同名變數
# 3.內部修改這個變數如果想在外部有這個變數的第一層函數中生效
def f1(): a = 1 def f2(): nonlocal a a = 2 f2() print(‘a in f1 : ‘,a)f1()
返回頂部
函數名的本質
函數名本質上就是函數的記憶體位址
1.可以被引用
def func(): print(‘in func‘)f = funcprint(f)
2.可以被當作容器類型的元素
def f1(): print(‘f1‘)def f2(): print(‘f2‘)def f3(): print(‘f3‘)l = [f1,f2,f3]d = {‘f1‘:f1,‘f2‘:f2,‘f3‘:f3}#調用l[0]()d[‘f2‘]()
3.可以當作函數的參數和傳回值
*不明白?那就記住一句話,就當普通變數用
第一類對象(first-class object)指1.可在運行期建立2.可用作函數參數或傳回值3.可存入變數的實體。
返回頂部
閉包
def func(): name = ‘eva‘ def inner(): print(name)
閉包函數:
內建函式包含對外部範圍而非全劇範圍名字的引用,該內建函式稱為閉包函數
#函數內部定義的函數稱為內建函式
由於有了範圍的關係,我們就不能拿到函數內部的變數和函數了。如果我們就是想拿怎麼辦呢?返回呀!
我們都知道函數內的變數我們要想在函數外部用,可以直接返回這個變數,那麼如果我們想在函數外部調用函數內部的函數呢?
是不是直接就把這個函數的名字返回就好了?
這才是閉包函數最常用的用法
def func(): name = ‘eva‘ def inner(): print(name) return innerf = func()f()
判斷閉包函數的方法__closure__
#輸出的__closure__有cell元素 :是閉包函數def func(): name = ‘eva‘ def inner(): print(name) print(inner.__closure__) return innerf = func()f()#輸出的__closure__為None :不是閉包函數name = ‘egon‘def func2(): def inner(): print(name) print(inner.__closure__) return innerf2 = func2()f2()
def wrapper(): money = 1000 def func(): name = ‘eva‘ def inner(): print(name,money) return inner return funcf = wrapper()i = f()i()
from urllib.request import urlopendef index(): url = "http://www.xiaohua100.cn/index.html" def get(): return urlopen(url).read() return getxiaohua = index()content = xiaohua()print(content)
返回頂部
本章小結
命名空間:
一共有三種命名空間從大範圍到小範圍的順序:內建命名空間、全域命名空間、局部命名空間
範圍(包括函數的範圍鏈):
小範圍的可以用大範圍的
但是大範圍的不能用小範圍的
範圍從大到小(圖)
在小範圍內,如果要用一個變數,是當前這個小範圍有的,就用自己的
如果在小範圍內沒有,就用上一級的,上一級沒有就用上上一級的,以此類推。
如果都沒有,報錯
函數的嵌套:
嵌套調用
嵌套定義:定義在內部的函數無法直接在全域被調用
函數名的本質:
就是一個變數,儲存了函數所在的記憶體位址
閉包:
內建函式包含對外部範圍而非全劇範圍名字的引用,該內建函式稱為閉包函數
python函數進階