1. 程式人生 > 其它 >python:從入門到放棄 10 裝飾器

python:從入門到放棄 10 裝飾器

目錄

裝飾器簡介

裝飾器的本質:在不改變被裝飾物件原有的呼叫方式內部程式碼、的情況下給被裝飾物件新增新的功能

軟體的設計應該遵循開放封閉原則,即對擴充套件是開放的,而對修改是封閉的。

軟體包含的所有功能的原始碼以及呼叫方式,都應該避免修改,否則一旦改錯,則極有可能產生連鎖反應,最終導致程式崩潰,而對於上線後的軟體,新需求或者變化又層出不窮,我們必須為程式提供擴充套件的可能性,這就用到了裝飾器。

裝飾器

# 首先定義一個函式
def foo():
    print("foo")
    
# 如果我們需要修改它的功能
def foo():
    print("記錄日誌開始")
    print("foo")
    print("記錄日誌結束")
'''可以看見的是,我們修改了他的內部程式碼,這有可能導致程式崩潰'''

# 使用裝飾器修改函式的功能
def outer(func): # 裝飾器
    def inner():
        print("記錄日誌開始")
        func() # 業務函式
        print("記錄日誌結束")
    return inner

def foo():
    print("foo") # foo函式的內部程式碼沒有被修改

foo = outer(foo) 
foo() # foo函式的呼叫方法沒有被修改
'''outer 函式的返回值是 inner 函式,在 inner 函式中,除了執行日誌操作,還有業務程式碼,該函式重新賦值給 foo 變數後,呼叫 foo() 就相當於呼叫 inner()'''

'''可以看見的是,outer函式就是一個裝飾器,即裝飾器是一個帶有函式作為引數並返回一個新函式的閉包'''

裝飾器模板

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

語法糖

# 僅僅是讓程式碼編寫的更加好看、簡潔!!!
@outer # foo = outer(foo) 省去了手動給foo重新賦值的步驟。
def foo():
    print("foo")

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

裝飾器修復技術

Python裝飾器在實現的時候,被裝飾後的函式其實已經是另外一個函數了(函式名等函式屬性會發生改變),而python的functools包中提供了一個叫wraps的裝飾器來消除這樣的副作用。它能保留原有函式的名稱和註釋。

from functools import wraps
def outer(func_name):
    @wraps(func_name) # 使用wraps裝飾器
    def inner(*args, **kwargs):
        '''這是inner函式的註釋'''
        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)
# home() 輸出的是home的函式名
# 這是home函式的註釋 輸出的是home的註釋  

如果我們將wraps裝飾器註釋

# from functools import wraps
def outer(func_name):
    # @wraps(func_name) # 不使用wraps裝飾器
    def inner(*args, **kwargs):
        '''這是inner函式的註釋'''
        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)
# inner(*args, **kwargs) 輸出的是inner的函式名
# 這是inner函式的註釋 輸出的是inner的註釋