11.函式物件,函式巢狀,名稱空間與作用域,閉包函式,裝飾器
阿新 • • 發佈:2020-12-29
-
引子
-
函式物件
-
函式巢狀
-
名稱空間與作用域
-
閉包函式
-
裝飾器
-
函式物件
在python中,函式是第一類物件,即函式可以當做資料傳遞,函式又稱第一等公民
本質:函式可以當變數用
-
1.可以賦值
def func(): # func = 函式的記憶體地址
print('from func')
f = func # func 可以賦值給其它變數
print(f)
f()
-
2.可以當做引數傳給另外一個函式
def func(): print('from func') def foo(x): print(x) x() # 可以加()觸發內部程式碼的執行 foo(func)
-
3.可以當做函式的返回值
def func():
print('from func')
def foo(x): # 把func丟進去
return x # 緊接著又扔出來func
res = foo(func)
print(res)
-
4.可以當做容器型別的元素
def func(): print('from func') l = [func,] # 列表呼叫 print(l) l[0]() # 容器型別的元素的應用 # 購物車程式 def withdraw(): print("提款") def tranfer(): print("轉賬") def check_balance(): print("查詢餘額") def save(): print("存款") func_dic = { "1":["提款",withdraw], "2":["轉賬",tranfer], "3":["查詢餘額",chenk_balance], "4":["存款",save] } while True: print("0 退出") for k,v in func_dic.items(): print(k,v[0]) choice = input("請輸入您要操作的編號>>>:").strip() if choice == '0': break if choice in func_dic: func_dic[choice][1]() else: print("輸入錯誤")
-
函式巢狀
-
函式的巢狀呼叫
def max2(x,y): # 比較兩個值的大小
if x > y:
return x
else:
return y
def max4(a,b,c,d): # 求四個值的最大值
res1 = max2(a,b)
res2 = max2(res1,c)
res3 = max2(res2,d)
return res3
print(max4(11,99,33,12))
# 把一個大功能拆解成幾個小功能,然後再把每個小功能分別去實現,最後在拼接回來
-
函式的巢狀定義
def f1():
x = 10 # 變數丟到函式裡面是一個封閉的效果
def f2():
print('from f2')
print(x) #1 這兩行程式碼是站在內部去看的可以看見
print(f2) #2
f1()
print(x) #1 這兩行程式碼是站在外面去看的是看不見的
print(f2) #2
# 函式層級帶來的變數訪問的限制,到底是因為什麼?定義在某一個位置的東西到底在哪才能看得見?
-
名稱空間與作用域
-
namespaces名稱空間:存放名字的地方
內建名稱空間:存放內建的名字
生命週期:python直譯器啟動則產生,關閉則銷燬
全域性名稱空間:存放的是頂級的名字
生命週期:執行python檔案時則產生,python檔案執行完畢則銷燬、
區域性名稱空間:存放的是函式內的名字
生命週期:呼叫函式則產生,函式呼叫完畢則銷燬
# 內建名稱空間:
>>> input
<built-in function input> # built-in 內建
# 全域性名稱空間:
x = 10 # 變數名X
y = 20 # 變數名y
f 1 > 0:
z = 30
with open('a.txt', mode='wt') as f:
a = 333
while True:
c = 444
# 以上都是屬於頂級的
# 區域性名稱空間:
x = 10 # 全域性名稱空間
def foo(m): # 全域性名稱空間
m = 111 # 呼叫函式時,才會執行函式程式碼,名字m和n都存放於該函式的區域性名稱空間中
n = 222 # 函式內
foo(111) # 呼叫產生函式名稱空間
名稱空間的載入順序是:內建名稱空間--->全域性名稱空間--->區域性名稱空間
而查詢一個名字,必須從三個名稱空間之一找到
查詢順序為:區域性名稱空間--->全域性名稱空間--->內建名稱空間
核心:名字的訪問優先順序
基於當前所在的位置向外查詢
# 案例1
len = 10 # 全域性名稱空間
def func(): # 函式
len = 20
# print(len) # 站在區域性查詢,找到的是20
func() # 呼叫產生區域性名稱空間
print(len) # 站在全域性找,全域性有找全域性,全域性沒有找內建
# 案例2
def f1():
x = 555
def f2():
x = 666
print(x)
f2()
x = 444
f1()
-
作用域(******)
名稱空間與作用域的關係是在函式定義階段(掃描語法時)就確立的,
與什麼時候呼叫以及呼叫位置無關
x = 111
def f1():
print(x)
def f2():
x = 222
f1()
f2()
# 案例2
x = 111
def f1():
print(x)
x = 222
f1()
-
作用域
全域性作用域:內建名稱空間 + 全域性名稱空間
特點:全域性存活(除非被刪除,否則在整個檔案執行過程中存活)
全域性有效(在任意位置都可以使用)
區域性作用域:區域性名稱空間
特點:臨時存活(即在函式呼叫時臨時生成,函式呼叫結束後就釋放)
區域性有效(只能在函式內使用)
-
global關鍵字
在函式內,無論巢狀多少層,都可以檢視到全域性作用域的名字,若要在函式內修改全域性名稱空間中名字的值,當值為不可變型別時,則需要用到global關鍵字
# 案例1:
l = []
def func():
l.append(1111)
# l = [11,22,33]
func() # 調func執行append
print(l)
# 當全域性變數是可變型別的時候,區域性是可以直接改的
# 案例2:
x = 111 # 全域性變數不可變型別
def func():
global x # 宣告X是屬於全域性的,是可以改變的
x = 222 # 在區域性產生新的名字不影響全域性
func()
print(x)
# 要想在區域性修改一個全域性的不可變型別,可以用global修改
-
nonlocal關鍵字
對於巢狀多層的函式,使用nonlocal關鍵字可以將名字宣告為來自外層巢狀函式定義的作用域(非全域性)
x = 111
def f1():
# x = 222
def f2():
# global x # 宣告變數名是來自全域性的
nonlocal x # 宣告變數名是來自於外層函式的,不是全域性
x = 333 # 要想在這個位置用global把x=222改掉不可能
f2()
print(x)
f1()
-
閉包函式
閉:指的該函式是定義在函式內的函式
包:指的就是該函式引用了一個外層函式作用域的名字(e作用域的名字)
def outter():
x = 111 # 為wrapper函式的函式體程式碼傳參
def wrapper():
print(x)
# 以後無論wrapper在哪調,它訪問的x以定義階段為準。
def outter(): # outter最外層函式
x = 111
def wrapper(): # 把wrapper閉到了outter裡面了,wrapper就只能在裡面用
print(x)
return wrapper # 千萬別加括號,要想把wrapper從裡面扔出來用就需要用return
f = outter() # 把一個內部函式扔到全域性,這個內部函式就打破了層級限制,可以在任意位置呼叫
print(f)
def foo():
x = 222
f() # 此時調f就相當於調wrapper
foo()
-
為函式程式碼體傳參的方案
方案一:直接用引數傳
def wrapper(x):
print(x)
wrapper(111)
wrapper(222)
wrapper(333)
方案二:閉包函式
def outter(x): # 為了給wrapper函式傳參只能把它扔進去,wrapper本來是頂級的
def wrapper():
print(x)
return wrapper # 引數傳完就把wrapper用return扔出去
f1 = outter(111) # 再想拿到wrapper,調outter拿到返回值,f1相當於當初的wrapper
f1()
f2 = outter(222)
f2()
-
裝飾器
-
1、什麼是裝飾器
裝飾器就是一個用來為被裝飾物件新增新功能的工具
-
2、為何要用裝飾器
開放封閉原則:一旦軟體上線執行之後,應該對修改原始碼封閉,對擴充套件功能開放
原則:
1、不修改函式內的原始碼
2、不修改函式的呼叫方式
裝飾器就是在遵循原則1和2的前提下,為被裝飾物件新增上新功能
-
3、如何實現裝飾器
函式裝飾器分為:無參裝飾器和有參裝飾兩種,二者的實現原理一樣,都是
“函式巢狀+閉包+函式物件”的組合使用的產物
# 需求:為函式index新增統計執行時間的功能
import time # 匯入時間模組
def index():
time.sleep(1)
print('from index')
index()
方案一:
import time
def index():
start = time.time() # 開始時間
time.sleep(1)
print('from index')
stop = time.time()
print('run time is %s' %(stop - start))
index()
方案二:
import time
def index():
time.sleep(1)
print('from index')
start = time.time()
index()
stop = time.time()
print('run time is %s' %(stop - start))
方案三:
import time
def index():
time.sleep(1)
print('from index')
def wrapper():
start = time.time()
index()
stop = time.time()
print('run time is %s' %(stop - start))
wrapper()
wrapper()
# 方案四:
import time
def index():
time.sleep(1)
print('from index')
def wrapper(func):
start = time.time()
func()
stop = time.time()
print('run time is %s' %(stop - start))
wrapper(index)
方案五:====》閉包函式
import time
def index():
time.sleep(1)
print('from index')
def wrapper():
start = time.time()
func()
stop = time.time()
print('run time is %s' %(stop - start))