1. 程式人生 > 其它 >函式 裝飾器 python

函式 裝飾器 python

 

 

今日內容概要

1.閉包函式

2.閉包函式的實際應用

3.裝飾器簡介(重點加難點)

4.簡易版本裝飾器

5.進階版本裝飾器

6.完整版本裝飾器

7.裝飾器模板(拷貝使用即可)

8.裝飾器語法糖

9.裝飾器修復技術

今日內容詳細

閉包函式(重要)

# 閉包函式的兩大特徵
1.閉:定義在函式內部的函式
2.包:內部函式使用了外層函式名稱空間中的名字

def outer():
   a = 999
   def inner():
       print('from outer>>>inner',a)
   return inner
a = 888
res = outer()
res()

閉包函式實際應用

# 閉包函式是給函式體傳參的另外一種方式
# 函式體傳參的方式
1. 形參
def index(user_name):
   print(user_name)
# 函式體程式碼需要什麼就可以在形參中寫什麼
index('zhou')

2.閉包
def outer():
   user_name = 'zhou'
   def index():
       print(user_name) # 永遠使用的都是zhou
   return index
res = outer()

def outer(user_name):
   # user_name = 'zhou'
   def index():
       print(user_name) # 永遠使用的都是zhou
   return index
res = outer('chen') # 形參user_name與值chen臨時繫結>>>:outer區域性名稱空間中
res()
res1 = outer('zhou') # 形參user_name與值zhou臨時繫結>>>:outer區域性名稱空間中
res1()

裝飾器簡介

"""
裝飾器並不是一個新的知識點 而是由前兩天所有的函式知識點整合到一起的產物
    名稱空間 函式名 閉包函式...
"""
裝飾器的本質
   在不改變被裝飾物件原有的‘呼叫方式’和‘內部程式碼’的情況下給被裝飾物件新增新的功能
 
裝飾器的原則: 對擴充套件開放 對修改封閉

import time
# print(time.time()) # 1647568920.168808
'''上述的數字是時間戳:1970年1月1日0時0分0秒距離剛剛程式碼執行間隔的秒數'''
# time.sleep(3)
# print('hello world')

# 需求:統一函式的執行時間
import time
def index():
   time.sleep(3)
   print('from index')
'''給index函式增加了一個統計執行時間的功能'''
start_time = time.time() # 函式執行之前獲取一個時間戳
index()
end_time = time.time() # 函式執行之後獲取一個時間戳
print(end_time - start_time) # 兩個時間戳的差值就是函式的執行時間

 

 

 

簡易版本裝飾器

import time
def index():
   time.sleep(1)
   print('from index')
'''給index函式增加了一個統計執行時間的功能'''
start_time = time.time() # 函式執行之前獲取一個時間戳
index()
end_time = time.time() # 函式執行之後獲取一個時間戳
print(end_time - start_time) # 兩個時間戳的差值就是函式的執行時間

思考:如果在很多地方都需要呼叫index,如何統計index執行時間
  >>> 在很多地方都需要執行統計index函式執行時間的程式碼
  >>> 在不同的地方需要執行相同的程式碼
  >>> 函式
   
直接通過傳參的方式
 缺陷1:
   程式碼寫死了 無法統計其他函式的執行時間
   能否解決?
     可以 將函式名通過形參的形式傳入
 缺陷2:
   封裝成函式之後 呼叫方式改變了 不符合裝飾器原則
   能否解決?
     不可以
       
def get_time(func):
   start_time = time.time() # 函式執行之後獲取一個時間戳
   func()
   end_time = time.time() # 函式執行之後獲取一個時間戳
   print(end_time - start_time) # 兩個時間戳的差值就是函式的執行時間
def home():
   time.sleep(3)
   print('from home')
get_time(index)
get_time(home)
'''
第一種直接給函式體傳參的方式無法實現裝飾器
只能採用第二種給函式體傳參的方式試試看了
'''

 

 


import time
def index():
   time.sleep(1)
   print('from index')
def home():
   time.sleep(3)
   print('from home')
print(home)
def outer(func): # 真正的index被outer區域性名稱空間儲存了
   def get_time():
       start_time = time.time() # 函式執行之前獲取一個時間戳
       func() # 呼叫了真正的index函式
       end_time = time.time() # 函式執行之後獲取一個時間戳
       print(end_time - start_time) # 兩個時間戳的差值就是函式的執行時間
   return get_time
res = outer(index) # 左側的res就是一個普通的變數名
res()
a = outer(index) # 左側的a就是一個普通的變數名
a()
b = outer(index) # 左側的b就是一個普通的變數名
b()
index = outer(index)
index() # 看似呼叫的是index 其實呼叫的誰是get_time
print(index) # 全域性名稱空間中的index指向的是get_time函式體程式碼
home = outer(home) # 狸貓換太子

 

進階版本裝飾器
# 解決的是引數問題
def outer(func_name):
   def get_time(*args,**kwargs):
       start_time = time.time()
       res = func_name(*args,**kwargs) # 執行真正的index函式
       end_time = time.time()
       print(end_time - start_time)
       # return '不要急躁' # 如何在此處返回真正index函式的返回值
       return res
   return get_time

 

 

 

裝飾器模板(重要)

'''編寫裝飾器其實有一套固定的程式碼 不需要做任何理解'''

def outer(func_name): # func_name用於接收裝飾的物件(函式)
   def inner(*args,**kwargs):
       print('執行被裝飾函式之前 可以做的額外操作')
       res = func_name(*args,**kwargs) # 執行真正的被裝飾函式
       print('執行被裝飾函式之後 可以做的額外操作')
       return res # 返回真正函式的返回值
   return inner

 

 

 

裝飾器語法糖

# 僅僅是讓程式碼編寫的更加好看,簡潔

def outer(func_time):
   def inner(*args,**kwargs):
       print('執行函式之前的操作')
       res = func_name(*args,**kwargs)
       # 額外操作
       return res
   return inner

@outer # 等價於 index = outer(index)
def index(*args,**kwargs):
   print('from index')
index = outer(index) # 總感覺這一行程式碼有點low

@outer # 等價於 home = outer(home)
def home(*args,**kwargs):
   print('from home')
   
print(index)
print(home)

'''
語法糖內部原理
  1.使用的時候最好緊跟在被裝飾物件的上方
  2.語法糖會自動將下面緊挨著的函式名傳給@後面的函式呼叫
 
'''

裝飾器修復技術

from functools import warps
def outer(func_name):
   @warps(func_name)
   def inner(*args,**kwargs):
       print('執行被裝飾物件之前可以做的操作')
       res = func_name(*args,**kwargs)
       return res
   return inner
@outer
def index():
   print('from index')
@outer
def home():
   '''這是home函式的註釋'''
   print('from home')
home()
index()

最終整理的裝飾器模板

# 簡易版裝飾器

def outer(func_name):   #  1. 定義外部函式
            def inner():    #  3.定義內部函式
            start_time = time.time()    #  7. 獲取函式執行前的時間戳
            func_name()    #  8. 呼叫函式,內部函式呼叫外部函式繫結的形參,由於第2步形參傳值,func_name與index臨時繫結到一起,且這裡是先呼叫outer函式再賦值,所以func_name繫結的是上面的函式名index,即呼叫函式index()
            end_time = time.time()   #  9. 獲取函式執行後的時間戳
            print(end_time - start_time)   #  輸出函式執行前後時間戳的差值
      return inner    #  4. 把內部函式的函式名返回出去
index = outer(index)   #  2. 呼叫外部函式   #  5. 定義變數名index,讓index指向outer的返回值
index()    #  6. 呼叫函式,由於index指向inner,所以index()等價於inner(),即呼叫函式inner()