1. 程式人生 > >[Python]寫個帶引數的裝飾器

[Python]寫個帶引數的裝飾器

上篇文章 Python裝飾器為什麼難理解?從函式到裝飾器一步一步介紹了Python裝飾器的來由,不知你對裝飾器理解了沒有,強烈建議你自己動手寫個裝飾器應用到專案中加深理解。裝飾器可以很簡單,也可以很複雜,具體看業務場景,簡單裝飾器不帶任何引數,而帶引數的裝飾器則更靈活,還有一種更為複雜的叫類裝飾器。

哪些地方適合用裝飾器呢?但凡是在多個地方出現雷同的程式碼塊,且這些程式碼與核心業務沒有直接關聯的都可以用裝飾器來代替,裝飾器不僅能減少程式碼量,還使得程式碼邏輯更清晰、可讀性更強,你只需專注於業務邏輯處理就行了。

今天說說帶引數的裝飾器,為了簡化業務邏輯,我們實現字串大寫轉換的需求,重點關注裝飾器部分:

# 業務函式
def my_upper(text):
    value = text.upper()
    return value

print(my_upper("hello"))  # HELLO

現在需求有變更,核心業務不變,但是需要對轉換的的字元包裹一層HTM標籤,輸出如: <p>HELLO</p>,最簡單的辦法就是直接在函式裡面修改邏輯,如:

def my_upper(text):
    value = text.upper()
    return "<p>" + value + "</p>"

又接到產品通知,需求有變更,還要在外面套一個div,於是你很不情願地回去再修改:

def my_upper(text):
    value = text.upper()
    return "<div><p>" + value + "</p></div>"

如何應對產品這種無止境的修改呢?

image

玩笑開完了,技術還是要為業務服務啊,那我們就想一個可以靈活應對產品的辦法吧,這裡,裝飾器就是一個很好的方案。最終效果應該是這樣:

@tag("p")
def my_upper(text):
    value = text.upper()
    return value

print(my_upper("hello")) #<p>HELLO</p>

如何實現呢?先從簡單裝飾器開始,實現一個不帶引數的裝飾器

def tag(func):
    def wrapper(text):
        value = func(text)
        return "<p>" + value + "</p>"

    return wrapper

@tag
def my_upper(text):
    value = text.upper()
    return value

呼叫

print(my_upper("hello")) # <p>hello</p>

@tag 語法糖等價於  my_upper = tag(my_upper)

my_upper("hello") 等價於 wrapper("hello") 儘管你不能直接訪問wrapper,但可以這樣去理解

使用裝飾器,業務程式碼一行的都沒改,只需要在函式定義處加上裝飾器,就實現了相同的功能,那麼如何更靈活地通過引數來指定輸出的樣式呢?使用帶引數的裝飾器

帶引數的裝飾器

帶引數的裝飾器只需要在原來那個不帶引數的裝飾器基礎上之上在最外層套一個函式,該函式中定義一個引數,然後巢狀函式中引用該引數即可實現。從下圖看出,我只是把裡面那個函式改了一下名字,其餘和原來不帶引數的裝飾器是一樣的。有沒有覺得這樣更靈活?

decorator-param.png

當然,裝飾器不僅可以修飾函式,還可以修飾類。

娛樂時間

最後留給大家一個問題,給函式實現一個日誌記錄功能,日誌裡面記錄函式名,函式執行所花的時間,通過指定引數控制日誌級別,第一個寫出來且正確的讀者可以找我領取10元紅包。考慮到程式碼在留言區顯示不好好,你可以使用 https://gist.github.com/ 然後直接回復程式碼連結就可以。(此活動僅在公眾號有效)


關注公眾號「Python之禪」(id:vttalk)獲取最新文章 python之禪