裝飾器 decorator

裝飾器定義

在不改變原有函數代碼,且保持原函數調用方法不變的情況下,給原函數增加新的功能(或者給類增加屬性和方法)

核心思想:用一個函數(或者類)去裝飾一箇舊函數(或者類),造出一個新函數(或者新類) 應用場景:引入日誌,函數執行時間的統計,執行函數前的準備工作,執行函數後的處理工作,權限校驗,緩存等 語法規則:在原有的函數上加上 @符,裝飾器會把下面的函數當作參數傳遞到裝飾器中,@符又被成為 語法糖

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>


作業:如何用一個裝飾器,裝飾帶有返回值的函數


分享到:


相關文章: