10、python裝飾器函數(shù)

裝飾器函數(shù)

楔子

作為一個會寫函數(shù)的python開發(fā),我們從今天開始要去公司上班了。寫了一個函數(shù),就交給其他開發(fā)用了。

def func1():

? ? print('in func1')

季度末,公司的領導要給大家發(fā)績效獎金了,就提議對這段日子所有人開發(fā)的成果進行審核,審核的標準是什么呢?就是統(tǒng)計每個函數(shù)的執(zhí)行時間。

這個時候你要怎么做呀?

你一想,這好辦,把函數(shù)一改:

import timedef func1():

? ? start = time.time()

? ? print('in func1')

? ? print(time.time() - start)

func1()

來公司半年,寫了2000+函數(shù),挨個改一遍,1個禮拜過去了,等領導審核完,再挨個給刪了。。。又1個禮拜過去了。。。這是不是很鬧心?

你覺得不行,不能讓自己費勁兒,告訴所有開發(fā),現(xiàn)在你們都在自己原本的代碼上加上一句計算時間的語句?

import timedef func1():

? ? print('in func1')

start = time.time()

func1()print(time.time() - start)

還是不行,因為這樣對于開發(fā)同事來講實在是太麻煩了。

那怎么辦呢?你靈機一動,寫了一個timer函數(shù)。。。

import timedef timer(func):

? ? start = time.time()

? ? func()

? ? print(time.time() - start)def func1():

? ? print('in func1')def func2():

? ? print('in func2')

timer(func1)

timer(func2)

這樣看起來是不是簡單多啦?不管我們寫了多少個函數(shù)都可以調(diào)用這個計時函數(shù)來計算函數(shù)的執(zhí)行時間了。。。盡管現(xiàn)在修改成本已經(jīng)變得很小很小了,但是對于同事來說還是改變了這個函數(shù)的調(diào)用方式,假如某同事因為相信你,在他的代碼里用你的方法用了2w多次,那他修改完代碼你們友誼的小船也就徹底地翻了。

你要做的就是,讓你的同事依然調(diào)用func1,但是能實現(xiàn)調(diào)用timer方法的效果。

import timedef timer(func):

? ? start = time.time()

? ? func()

? ? print(time.time() - start)def func1():

? ? print('in func1')

func1 =timer? #要是能這樣的就完美了。。??上箦efunc1()

非??上?,上面這段代碼是會報錯的,因為timer方法需要傳遞一個func參數(shù),我們不能在賦值的時候傳參,因為只要執(zhí)行func1 = timer(func1),timer方法就直接執(zhí)行了,下面的那句func1根本就沒有意義。到這里,我們的思路好像陷入了僵局。。。


裝飾器的形成過程

import timedef func1():

? ? print('in func1')def timer(func):

? ? def inner():

? ? ? ? start = time.time()

? ? ? ? func()

? ? ? ? print(time.time() - start)

? ? return inner

func1 = timer(func1)

func1()

忙活了這么半天,終于初具規(guī)模了!現(xiàn)在已經(jīng)基本上完美了,唯一礙眼的那句話就是還要在做一次賦值調(diào)用。。。

你覺得礙眼,python的開發(fā)者也覺得礙眼,所以就為我們提供了一句語法糖來解決這個問題!

import timedef timer(func):

? ? def inner():

? ? ? ? start = time.time()

? ? ? ? func()

? ? ? ? print(time.time() - start)

? ? return inner

@timer? #==> func1 = timer(func1)def func1():

? ? print('in func1')

func1()

到這里,我們可以簡單的總結一下:

  裝飾器的本質(zhì):一個閉包函數(shù)

  裝飾器的功能:在不修改原函數(shù)及其調(diào)用方式的情況下對原函數(shù)功能進行擴展

還有最后一個問題要解決,剛剛我們討論的裝飾器都是裝飾不帶參數(shù)的函數(shù),現(xiàn)在要裝飾一個帶參數(shù)的函數(shù)怎么辦呢?

def timer(func):

? ? def inner(a):

? ? ? ? start = time.time()

? ? ? ? func(a)

? ? ? ? print(time.time() - start)

? ? return inner

@timerdef func1(a):

? ? print(a)

func1(1)

現(xiàn)在參數(shù)的問題已經(jīng)完美的解決了,可是如果你的函數(shù)是有返回值的呢?

import timedef timer(func):

? ? def inner(*args,**kwargs):

? ? ? ? start = time.time()

? ? ? ? re = func(*args,**kwargs)

? ? ? ? print(time.time() - start)

? ? ? ? return re

? ? return inner

@timer? #==> func2 = timer(func2)def func2(a):

? ? print('in func2 and get a:%s'%(a))

? ? return 'fun2 over'func2('aaaaaa')print(func2('aaaaaa'))

其實裝飾帶參的函數(shù)并不是什么難事,但假如你有兩個函數(shù),需要傳遞的參數(shù)不一樣呢?

import timedef timer(func):

? ? def inner(*args,**kwargs):

? ? ? ? start = time.time()

? ? ? ? re = func(*args,**kwargs)

? ? ? ? print(time.time() - start)

? ? ? ? return re

? ? return inner

@timer? #==> func1 = timer(func1)def func1(a,b):

? ? print('in func1')

@timer? #==> func2 = timer(func2)def func2(a):

? ? print('in func2 and get a:%s'%(a))

? ? return 'fun2 over'func1('aaaaaa','bbbbbb')print(func2('aaaaaa'))

剛剛那個裝飾器已經(jīng)非常完美了,但是正常我們情況下查看函數(shù)的一些信息的方法在此處都會失效

def index():

? ? '''這是一個主頁信息'''? ? print('from index')print(index.__doc__)? ? #查看函數(shù)注釋的方法print(index.__name__)? #查看函數(shù)名的方法

為了不讓他們失效,我們還要在裝飾器上加上一點來完善它:

from functools import wrapsdef deco(func):

? ? @wraps(func) #加在最內(nèi)層函數(shù)正上方? ? def wrapper(*args,**kwargs):

? ? ? ? return func(*args,**kwargs)

? ? return wrapper

@decodef index():

? ? '''哈哈哈哈'''? ? print('from index')print(index.__doc__)print(index.__name__)

開放封閉原則

  1.對擴展是開放的

    為什么要對擴展開放呢?

    我們說,任何一個程序,不可能在設計之初就已經(jīng)想好了所有的功能并且未來不做任何更新和修改。所以我們必須允許代碼擴展、添加新功能。

  2.對修改是封閉的

    為什么要對修改封閉呢?

    就像我們剛剛提到的,因為我們寫的一個函數(shù),很有可能已經(jīng)交付給其他人使用了,如果這個時候我們對其進行了修改,很有可能影響其他已經(jīng)在使用該函數(shù)的用戶。

裝飾器完美的遵循了這個開放封閉原則。

裝飾器的主要功能和裝飾器的固定結構

裝飾器的主要功能:

在不改變函數(shù)調(diào)用方式的基礎上在函數(shù)的前、后添加功能。

裝飾器的固定格式:

def timer(func):

? ? def inner(*args,**kwargs):

? ? ? ? '''執(zhí)行函數(shù)之前要做的'''? ? ? ? re = func(*args,**kwargs)

? ? ? ? '''執(zhí)行函數(shù)之后要做的'''? ? ? ? return re

? ? return inner

from functools import wrapsdef deco(func):

? ? @wraps(func) #加在最內(nèi)層函數(shù)正上方? ? def wrapper(*args,**kwargs):

? ? ? ? return func(*args,**kwargs)

? ? return wrapper


帶參數(shù)的裝飾器

假如你有成千上萬個函數(shù)使用了一個裝飾器,現(xiàn)在你想把這些裝飾器都取消掉,你要怎么做?

一個一個的取消掉? 沒日沒夜忙活3天。。。

過兩天你領導想通了,再讓你加上。。。

def outer(flag):

? ? def timer(func):

? ? ? ? def inner(*args,**kwargs):

? ? ? ? ? ? if flag:

? ? ? ? ? ? ? ? print('''執(zhí)行函數(shù)之前要做的''')

? ? ? ? ? ? re = func(*args,**kwargs)

? ? ? ? ? ? if flag:

? ? ? ? ? ? ? ? print('''執(zhí)行函數(shù)之后要做的''')

? ? ? ? ? ? return re

? ? ? ? return inner

? ? return timer

@outer(False)def func():

? ? print(111)

func()

多個裝飾器裝飾同一個函數(shù)

有些時候,我們也會用到多個裝飾器裝飾同一個函數(shù)的情況

def wrapper1(func):

? ? def inner():

? ? ? ? print('wrapper1 ,before func')

? ? ? ? func()

? ? ? ? print('wrapper1 ,after func')

? ? return innerdef wrapper2(func):

? ? def inner():

? ? ? ? print('wrapper2 ,before func')

? ? ? ? func()

? ? ? ? print('wrapper2 ,after func')

? ? return inner

@wrapper2

@wrapper1def f():

? ? print('in f')

f()

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 寫在前面的話 代碼中的# > 表示的是輸出結果 輸入 使用input()函數(shù) 用法 注意input函數(shù)輸出的均是字...
    FlyingLittlePG閱讀 3,214評論 0 9
  • 包(lib)、模塊(module) 在Python中,存在包和模塊兩個常見概念。 模塊:編寫Python代碼的py...
    清清子衿木子水心閱讀 3,908評論 0 27
  • 一、快捷鍵 ctr+b 執(zhí)行ctr+/ 單行注釋ctr+c ...
    o_8319閱讀 6,032評論 2 16
  • 原以為癌癥離我那么遙遠,卻不知就在身邊隨時出現(xiàn)。半年來的口腔潰瘍反反復復吃點藥就好了。藥停了要不了半個月又復發(fā),剛...
    夕陽晚晴閱讀 546評論 0 0
  • (月里嫦娥)天宮有園有瓊樓,人間無房望高樓??偭w鴛鴦戲成雙,有愁宮闕獨寒門。花若有情為何枯,人如無愛淚沾衣。月園月...
    甘朝武閱讀 373評論 0 0

友情鏈接更多精彩內(nèi)容