1. 閉包
在函數(shù)內(nèi)部再定義一個函數(shù),并且內(nèi)部函數(shù)用到了外部函數(shù)作用域里的變量(enclosing),那么將這個內(nèi)部函數(shù)以及用到的外部函數(shù)內(nèi)的變量一起稱為閉包(Closure)
基本格式:
def 外部函數(shù)名():
內(nèi)部函數(shù)需要的變量
def 內(nèi)部函數(shù)名()
引用外部的變量
return 內(nèi)部函數(shù)
- 注意:
函數(shù)的作用域關(guān)系在函數(shù)定義階段就已經(jīng)固定,與調(diào)用位置無關(guān)。
無論函數(shù)在何處調(diào)用,都需要回到定義階段去找對應(yīng)的作用域關(guān)系。
閉包中不要引用外部函數(shù)中任何循環(huán)變量或后續(xù)會發(fā)生變化的變量(延遲計算)
2. 裝飾器
2.1 裝飾器語法糖
@ 符號就是裝飾器的語法糖
它放在一個函數(shù)開始定義的地方,它就像一頂帽子一樣戴在這個函數(shù)的頭上。和這個函數(shù)綁定在一起。在我們調(diào)用這個函數(shù)的時候,第一件事并不是執(zhí)行這個函數(shù),而是將這個函數(shù)做為參數(shù)傳入它頭頂上這頂帽子,這頂帽子我們稱之為裝飾函數(shù) 或 裝飾器
裝飾器的使用方法很固定:
(1) 先定義一個裝飾函數(shù)(帽子)(也可以用類、偏函數(shù)實(shí)現(xiàn))
(2) 再定義你的業(yè)務(wù)函數(shù)、或者類(人)
(3) 最后把這頂帽子帶在這個人頭上
2.2 裝飾器無參數(shù)
格式:
def 裝飾器外部函數(shù)(func): #func 就是被裝飾函數(shù)
def裝飾器內(nèi)部函數(shù)(*args, **kwargs): #裝飾器內(nèi)部函數(shù)是一個閉包,它使用了外部函數(shù)的變量func裝飾函數(shù)的功能
裝飾器的功能
return func(*args, **kwargs) #執(zhí)行被裝飾函數(shù)并返回被裝飾函數(shù)的返回值
return 返回裝飾器內(nèi)部函數(shù)名 #注!返回裝飾器內(nèi)部函數(shù)名而不是裝飾器內(nèi)部函數(shù)
@裝飾器外部函數(shù)名 #將被裝飾函數(shù)傳入裝飾器,返回裝飾器內(nèi)部函數(shù)引用
def 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)):
被裝飾函數(shù)的功能
return 被裝飾函數(shù)的返回值
接收返回值 = 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)) #執(zhí)行函數(shù),即執(zhí)行裝飾器內(nèi)部函數(shù),這個閉包使用的func變量指向被裝飾函數(shù)體
print(接收返回值) #打印一下接收的返回值
2.4 裝飾器帶參數(shù)
根據(jù)功能需求,在原來的裝飾器外面再加一層函數(shù)
格式:
def 需求函數(shù)(需求函數(shù)參數(shù)):
def 裝飾器外部函數(shù)(func):
def 裝飾器內(nèi)部函數(shù)(*args, **kwargs):
裝飾器的功能
return func(*args, **kwargs)
return 裝飾器內(nèi)部函數(shù)
return 裝飾器外部函數(shù)
@需求函數(shù)('需求函數(shù)參數(shù)') # 等價于 被裝飾函數(shù) = 需求函數(shù)('需求函數(shù)參數(shù)')(被裝飾函數(shù)) ,即先執(zhí)行需求函數(shù)('需求函數(shù)參數(shù)'),返回裝飾
#(接上一行的解釋)器外部函數(shù)引用(真正的裝飾器),再用裝飾器外部函數(shù)裝飾被裝飾函數(shù),返回裝飾器內(nèi)部函數(shù)
def 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)):
被裝飾函數(shù)的功能
return 被裝飾函數(shù)的返回值
接收返回值 = 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)) #執(zhí)行函數(shù),即執(zhí)行裝飾器內(nèi)部函數(shù),這個閉包使用的func變量指向被裝飾函數(shù)體
print(接收返回值) #打印一下接收的返回值
2.5 使用@wraps
當(dāng)打印被裝飾函數(shù)函數(shù)名時,因為被裝飾函數(shù)是在裝飾器內(nèi)部函數(shù)調(diào)用所以顯示裝飾器內(nèi)部函數(shù)名,想打印被裝飾函數(shù)名時可以使用functools模塊的wraps裝飾器解決這個問題
格式:
from functools import wraps
def 需求函數(shù)(需求函數(shù)參數(shù)):
def 裝飾器外部函數(shù)(func):
@wraps(func)
def 裝飾器內(nèi)部函數(shù)(*args, **kwargs):
裝飾器的功能
return func(*args, **kwargs)
return 裝飾器內(nèi)部函數(shù)
return 裝飾器外部函數(shù)
@需求函數(shù)('需求函數(shù)參數(shù)') # 等價于 被裝飾函數(shù) = 需求函數(shù)('需求函數(shù)參數(shù)')(被裝飾函數(shù)) ,即先執(zhí)行需求函數(shù)('需求函數(shù)參數(shù)'),返回裝飾
#(接上一行解釋)器外部函數(shù)引用(真正的裝飾器),再用裝飾器外部函數(shù)裝飾被裝飾函數(shù),返回裝飾器內(nèi)部函數(shù)
def 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)):
被裝飾函數(shù)的功能
return 被裝飾函數(shù)的返回值
接收返回值 = 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)) #執(zhí)行函數(shù),即執(zhí)行裝飾器內(nèi)部函數(shù),這個閉包使用的func變量指向被裝飾函數(shù)體
print(接收返回值) #打印一下接收的返回值
2.6 多個裝飾器裝飾同一個函數(shù)
格式:
# 裝飾器1
def 裝飾器外部函數(shù)(func): #func 就是被裝飾函數(shù)
def裝飾器內(nèi)部函數(shù)(*args, **kwargs): #裝飾器內(nèi)部函數(shù)是一個閉包,它使用了外部函數(shù)的變量func裝飾函數(shù)的功能
裝飾器的功能
return func(*args, **kwargs) #執(zhí)行被裝飾函數(shù)并返回被裝飾函數(shù)的返回值
return 返回裝飾器內(nèi)部函數(shù)名 #注!返回裝飾器內(nèi)部函數(shù)名而不是裝飾器內(nèi)部函數(shù)
# 裝飾器2
def 裝飾器外部函數(shù)(func): #func 就是被裝飾函數(shù)
def裝飾器內(nèi)部函數(shù)(*args, **kwargs): #裝飾器內(nèi)部函數(shù)是一個閉包,它使用了外部函數(shù)的變量func裝飾函數(shù)的功能
裝飾器的功能
return func(*args, **kwargs) #執(zhí)行被裝飾函數(shù)并返回被裝飾函數(shù)的返回值
return 返回裝飾器內(nèi)部函數(shù)名 #注!返回裝飾器內(nèi)部函數(shù)名而不是裝飾器內(nèi)部函數(shù)
@裝飾器1的外部函數(shù)名 #將被裝飾函數(shù)傳入裝飾器,返回裝飾器內(nèi)部函數(shù)引用
@裝飾器2的外部函數(shù)名 #將被裝飾函數(shù)傳入裝飾器,返回裝飾器內(nèi)部函數(shù)引用
def 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)):
被裝飾函數(shù)的功能
return 被裝飾函數(shù)的返回值
接收返回值 = 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)) #執(zhí)行函數(shù),即執(zhí)行裝飾器內(nèi)部函數(shù),這個閉包使用的func變量指向被裝飾函數(shù)體
print(接收返回值) #打印一下接收的返回值
小結(jié):多個裝飾器的順序,包裝時,是從下往上的,也就是被裝飾函數(shù)頭上,哪個離得近就先包裝哪個
2.7 基于類實(shí)現(xiàn)的裝飾器
基于類裝飾器的實(shí)現(xiàn),必須實(shí)現(xiàn)__call__ 和 __init__兩個內(nèi)置函數(shù)。
2.7.1 不帶參數(shù)的類裝飾器
-
__init__:接收被裝飾函數(shù) -
__call__:實(shí)現(xiàn)裝飾邏輯
格式:
class 類裝飾器函數(shù)(object):
def __init__(self, func): #接收被裝飾函數(shù)
self.func = func
def __call__(self, *args, **kwargs): #實(shí)現(xiàn)裝飾邏輯
裝飾器函數(shù)的功能
return self.func(*args, **kwargs)
@類裝飾器函數(shù)名
def 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)):
被裝飾函數(shù)的功能
被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)) #執(zhí)行函數(shù)
2.7.2 帶參數(shù)的類裝飾器
帶參數(shù)和不帶參數(shù)的類裝飾器有很大的不同。
__init__:不再接收被裝飾函數(shù),而是接收傳入?yún)?shù)。
__call__ :接收被裝飾函數(shù),實(shí)現(xiàn)裝飾邏輯。
格式:
class 類裝飾器(object):
def __init__(self, level='類裝飾器函數(shù)參數(shù)'):
self.level = level
def __call__(self, func): # 接受函數(shù)
def 裝飾器內(nèi)部函數(shù)(*args, **kwargs):
裝飾器的功能
func(*args, **kwargs)
return 裝飾器內(nèi)部函數(shù)名 #返回函數(shù)
@類裝飾器函數(shù)名(level='類裝飾器函數(shù)參數(shù)')
def 被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)):
被裝飾函數(shù)的功能
被裝飾函數(shù)(被裝飾函數(shù)的參數(shù)) #執(zhí)行函數(shù)