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

python閉包函式和裝飾器

python閉包函式和裝飾器

閉包函式

閉包的特點就是內部函式引用了外部函式中的變數
# 閉包函式的兩大特徵
1.閉:定義在函式內部的函式
2.包:內部函式使用了外層函式名稱空間中的名字
    #程式碼示例:
    def outer():
    name = 'python'
    def inner():
        print(name)
    return inner

    index = outer()
    index()
    
'閉包中被內部函式引用的變數,不會因為外部函式結束而被釋放掉,而是一直存在記憶體中,知道內部函式被呼叫結束'

閉包函式的實際應用

#閉包好處:
  1. 保護變數
  2. 可以讓一個變數常駐記憶體
  3. 迭代器
    __iter__() 可迭代物件. 獲取迭代器
    __next__() + __iter__() 迭代器

  特點:
    1. 惰性機制
    2. 只能向前
    3. 節省記憶體
  for迴圈的內部就是迭代器

'閉包函式是給函式體傳參的另外一種方式'
#函式體傳參的方式1:形參
def index(username):
    print(username)
index('mark')

#函式體傳參的方式2:閉包
def outer():
    username = 'jason'
    def index():
        print(username)  
    return index
res = outer()


裝飾器簡介

# 裝飾器的本質
  在不改變被裝飾物件原有的'呼叫方式'和'內部程式碼'
  的情況下給被裝飾物件新增新的功能
# 裝飾器的原則
      對擴充套件開放
      對修改封閉
        
    #實質: 是一個函式

    #引數:是你要裝飾的函式名(並非函式呼叫)

    #返回:是裝飾完的函式名(也非函式呼叫)

    #作用:為已經存在的物件新增額外的功能

    #特點:不需要對物件做任何的程式碼上的變動

    
    
# 程式碼示例:
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)  # 兩個時間戳的差值就是函式的執行時間

簡易版本的裝飾器

直接通過傳參的方式
    缺陷1:
        程式碼寫死了 無法統計其他函式的執行時間
        能否解決?
            可以!  將函式名通過形參的形式傳入
    缺陷2:
        封裝成函式之後 呼叫方式改變了 不符合裝飾器原則

#程式碼示例:
import time
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(home)	#	from home 3.0071964263916016

'第一種直接給函式體傳參的方式無法實現裝飾器'

#程式碼示例:
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()

# index = outer(index)
# index()  # 看似呼叫的index其實呼叫的是get_time
# print(index)  # 全域性名稱空間中的index指向的是get_time函式體程式碼



# 簡易版裝飾器
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()

進階版本裝飾器

def outer(index):	
    def inner(*args,**kwargs):	#解決引數問題
        start_time = time.time()
        index()	
        end_time =time.time()
        print(end_time - start_time)
    return inner	

完整版本裝飾器

'解決返回值的問題'
#程式碼示例:
import time
def index():
    time.sleep(1)
    print('from index')
def outer(index):
    def inner(*args,**kwargs):
        start_time = time.time()
        res=index(*args,**kwargs) #這裡執行真正的index
        end_time =time.time()
        print(end_time - start_time)
        return #這裡返回真正index的返回值
    return inner

index = outer(index)
index()

裝飾器模板

'''編寫裝飾器其實有一套固定的程式碼 不需要做任何理解'''
def outer(func_name):  # func_name用於接收被裝飾的物件(函式)
    def inner(*args, **kwargs):
        print('執行被裝飾函式之前 可以做的額外操作')
        res = func_name(*args, **kwargs)  # 執行真正的被裝飾函式
        print('執行被裝飾函式之後 可以做的額外操作')
        return res  # 返回真正函式的返回值
    return inner

裝飾器的語法糖

# 讓程式碼編寫的更加好看、簡潔!!!

#無引數的語法糖
import time
def show_time(func):
    def inner():  #內部引數
        start_time =time.time()
        func() #呼叫函式
        end_time=time.time()
        print('伺服器響應時間:',end_time-start_time)
    return inner

@show_time #等價於func=show_time(func)
def func():
    print('執行用例')
    time.sleep(1)
#呼叫函式
func() 


#有引數的語法糖
import time #時間模組
def have_para(name): #引數名,執行者變數
    def show_time(func): #函式名呼叫變數
        def inner():#內部函式,函式體
            start_time =time.time()
            func()  #呼叫func()函式
            end_time=time.time()
            print('伺服器響應時間:',end_time-start_time)
            print('執行者:',name)
        return inner  #返回inner物件
    return show_time #返回show_time物件

@have_para('wendy') #等價於func=have_para('wendy')
def func():
    print('執行用例')
    time.sleep(1)
func()

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

裝飾器修復技術

# 做到比真的還要真  但是本質其實沒有變
from functools import wraps
def outer(func_name):
    @wraps(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')

help(home)  # help可以檢視指定函式的註釋資訊

home()
index()