裝飾器定義
在不改變原有函數代碼,且保持原函數調用方法不變的情況下,給原函數增加新的功能(或者給類增加屬性和方法) 核心思想:用一個函數(或者類)去裝飾一箇舊函數(或者類),造出一個新函數(或者新類) 應用場景:引入日誌,函數執行時間的統計,執行函數前的準備工作,執行函數後的處理工作,權限校驗,緩存等 語法規則:在原有的函數上加上 @符,裝飾器會把下面的函數當作參數傳遞到裝飾器中,@符又被成為 語法糖
1.裝飾器原型(閉包)
<code># 1。 裝飾器的原型
### 利用閉包,把函數當作參數傳遞,並且在函數內去調用傳遞進來的函數,並返回一個函數
# 定義外函數,接收一個函數作為參數
def outer(f):
# 定義內函數,並且在內函數中調用了外函數的參數
def inner():
print('我是外函數中的內函數1')
f()
print('我是外函數中的內函數2')
return inner
# 定義普通函數
# def old():
# print('我是一個普通的函數')
#
# # old() # 作為普通函數直接調用
# old = outer(old) # outer返回了inner函數,賦值給了old
# old() # 此時再調用old函數時,等同於調用了 inner 函數
# 改為裝飾器用法
@outer # 此處使用的@outer的語法就是把outer作為了裝飾器,等同於 old = outer(old)
def old():
print('我是一個普通的函數')
old() # old函數經過 outer裝飾器進行了裝飾,代碼和調用方法不變,但是函數的功能發送了改變/<code>
2.裝飾器的應用:統計函數的執行時間
<code># 裝飾器應用場景-統計函數執行時間
import time
# 定義一個統計函數執行時間的 裝飾器
def runtime(f):
def inner():
start = time.perf_counter()
f()
end = time.perf_counter() - start
print(f'函數的調用執行時間為:{end}')
return inner
# 定義一個函數
@runtime
def func():
for i in range(5):
print(i,end=" ")
time.sleep(1)
func()/<code>
3.裝飾器嵌套語法
<code># 1.定義裝飾器
# 外函數
def outer(func):
#內函數
def inner():
print('找到妹子,成功拿到微信。。。3')
func() # 在內函數中調用外函數中的行參-函數
print('約妹子,看一場午夜電影。。。4')
# 在外函數中返回內函數
return inner
# 2。在定義一個裝飾器
def kuozhan(f):
def kzinner():
print('擴展1')
f()
print('擴展2')
return kzinner
# 3. 裝飾器的嵌套 先執行下面的,再執行上面的。
@kuozhan # 2。再使用上面的 kuozhan 裝飾器,裝飾 上一次返回的 inner 函數,又返回了 kzinner 函數
@outer # 1。先使用離得近的 outer裝飾器 裝飾love函數,返回了一個 inner函數
def love():
print('跟妹子暢談人生和理想。。。5')
love()
''' 結果和過程的解析
1 3 5 4 2
1 先使用離得近的 outer裝飾器 裝飾love函數,返回了一個 inner函數
2 再使用上面的 kuozhan 裝飾器,裝飾 上一次返回的 inner 函數,又返回了 kzinner 函數
最後在調用love函數的時候是怎麼執行的
love() == kzinner()
===> 1
===> inner()
===> 3
===> love() ===> 5
===> 4
===> 2
'''/<code>
4.對帶有參數的函數進行裝飾
<code># 定義裝飾器
def outer(func):
# 如果裝飾器帶有參數的函數,需要在內函數中定義行參,並傳遞給調用的函數。因為調用原函數等於調用內函數
def inner(var):
print(f'找到{var}妹子,成功拿到微信。。')
func(var)
print(f'約{var}妹子,看一場午夜電影。。')
return inner
# 有參數的函數
@outer
def love(name):
print(f'跟{name}妹子暢談人生。。。')
love('思思') #love() ==> inner() love('思思') ===> inner('思思')
/<code>
5.對多參數的函數進行裝飾
<code># 裝飾帶有多參數的函數
def outer(func):
def inner(who,name,*args,**kwargs):
print('約到妹子,聊微信。。。')
func(who,name,*args,**kwargs)
print('天色一晚,怎麼辦?')
return inner
# 定義多參數的 函數
@outer
def love(who,name,*args,**kwargs):
print(f'{who}跟{name}暢談人生。。。')
print('完事去吃了好多美食',args)
print('看了一場電影',kwargs)
love('三多','思思','火鍋','辣條','7塊錢的麻辣燙',mov='唐山大地震')
'''
love() ==> inner()
love(...) ==> inner(...)
inner(...) ==> love(...)
'''/<code>
6.帶有參數的裝飾器
你會遇到帶有參數的裝飾器,例如Django框架中的 @login_required(login_url='/accounts/login/')
<code># 如果你的裝飾器需要有參數,那麼給當前的裝飾器套一個殼,用於接收裝飾器的參數
def kuozhan(var):
def outer(func):
def inner1():
print('妹子給了你微信')
func()
def inner2():
print('妹子給介紹了個大媽')
func()
# 裝飾器殼的參數,可以用於在函數內去做流程控制
if var == 1:
return inner1
else:
return inner2
return outer
@kuozhan(2) # kuozhan(var) ==> outer() ==> outer(love) ==> inner()
def love():
print('談談人生。。。')
love()/<code>
7.用類裝飾器裝飾函數
<code># 類裝飾器裝飾函數
class Outer():
# 魔術方法:當把該類的對象當作函數調用時,自動觸發 obj()
def __call__(self,func):
self.func = func # 把傳進來的函數作為對象的成員方法
return self.inner # 返回一個函數
# 在定義的需要返回的新方法中 去進行裝飾和處理
def inner(self,who):
print('拿到妹子的微信。。。')
self.func(who)
print('看一場午夜電影。。。')
@Outer() # Outer() ==> obj @obj==>obj(love) ==> __call__(love) ==> inner()
def love(who):
print(f'{who}和妹子談談人生和理想。。。')
love('川哥') # inner('川哥')
print(love) # 此時的 love就是屬於Outer類這個對象中的inner方法/<code>
8.用類方法裝飾函數
<code># 用類方法裝飾函數
class Outer():
def newinner(func):
Outer.func = func # 把傳遞進來的函數定義為類方法
return Outer.inner # 同時返回一個新的類方法
def inner():
print('拿到妹子微信')
Outer.func()
print('看一場午夜電影')
@Outer.newinner # Outer.newinner(love) ==> Outer.inner
def love():
print('和妹子談談人生喝喝茶。。。')
love() # love() ==> Outer.inner()/<code>
到目前為止以上所以形式的裝飾器,包括 函數裝飾器,類裝飾器,類方法裝飾器,都有一個共同特點:都是在給函數去進行裝飾,增加功能。
用裝飾器裝飾類
還有一種裝飾器,是專門裝飾類的。也就是在類的定義的前面使用@裝飾器這種語法 @裝飾器 class Demo(): pass
裝飾器給函數進行裝飾,目的是不改變函數調用和代碼的情況下給原函數增加了新的功能。
裝飾器給類進行裝飾,目的是不改變類的定義和調用的情況下給類增加新的成員(屬性或方法)。
9.用函數裝飾器裝飾類
<code># 使用函數裝飾器,給類進行裝飾,增加新的屬性和方法
# 定義函數,接收一個類。返回修改後的類
def kuozhan(cls):
def func2():
print('我是在裝飾器中追加的新方法,func2')
cls.func2 = func2 # 把剛才定義的方法賦值給 類
cls.name = '我是在裝飾器中追加的新屬性 name'
#返回時,把追加類新成員的 類 返回去
return cls
@kuozhan # kuozhan(Demo) ==> cls ==> Demo
class Demo():
def func():
print('我是Demo類中定義的func方法')
Demo.func() # 此時在調用的Demo類是通過裝飾器,更新過的Demo類
Demo.func2()
print(Demo.name)
/<code>
10.使用類裝飾器裝飾類
<code>class KuoZhan():
def __call__(self, cls):
# 把接收的類,賦值給當前對象,作為一個屬性
self.cls = cls
# 返回一個函數
return self.newfunc
def newfunc(self):
self.cls.name = '我是在類裝飾器中追加的新屬性 name'
self.cls.func2 = self.func2
# 返回傳遞進來的類的實例化結果,obj
return self.cls()
def func2(self):
print('我是在類裝飾器中追加的新方法 func2')
@KuoZhan() # KuoZhan() ==> obj ==> @obj(Demo) ==> __call__(Demo) ==> newfunc
class Demo():
def func(self):
print('我是Demo類中定義的func方法')
obj = Demo() # Demo() ==> newfunc() ==> obj
obj.func()
obj.func2()
print(obj.name)
# 思考: 此時的 obj這個對象,是哪個類的對象。Demo還是KuoZhan
print(obj) # 此時的obj依然是Demo類的實例化對象,只不過經過裝飾後,增加了新的屬性和方法
/<code>
作業:如何用一個裝飾器,裝飾帶有返回值的函數
閱讀更多 北京圖靈知非教育 的文章