python裝飾器

裝飾器簡(jiǎn)述

要理解裝飾器需要知道Python高階函數(shù)python閉包,Python高階函數(shù)可以接受函數(shù)作為參數(shù)懈费,也可以返回函數(shù)羹饰,閉包的內(nèi)部函數(shù)可以訪問外部函數(shù)的局部變量孝冒。Python的裝飾器正式基于高階函數(shù)和閉包尊惰。

來看一個(gè)簡(jiǎn)單的裝飾器:

def dec_func(fun):
    def wrapper():
        print('In Wapper!')
        fun()
        print('After fun')
    return wrapper
@dec_func
def foo():
    print('foo')
foo()
In Wapper!
foo
After fun

可以看到裝飾器不影響foo()函數(shù)的正常功能讲竿,它接受一個(gè)函數(shù)作為參數(shù),然后對(duì)這個(gè)函數(shù)進(jìn)行裝飾后返回給原來的函數(shù)名弄屡。

裝飾器的原理類似下面的函數(shù):

def bar():
    print('Bar')
bar = dec_func(bar)
bar()
In Wapper!
Bar
After fun

裝飾器的運(yùn)行首先是將函數(shù)帶入裝飾器题禀,原本的函數(shù)名接受裝飾器“裝飾”后的返回函數(shù),再調(diào)用這個(gè)返回函數(shù)琢岩。

裝飾器就是對(duì)函數(shù)進(jìn)行裝飾投剥,“裝飾”以為這并不會(huì)對(duì)函數(shù)的正常運(yùn)行造成影響师脂,僅僅是對(duì)函數(shù)的一些功能進(jìn)行額外的補(bǔ)充担孔。

裝飾器是一個(gè)函數(shù),接受一個(gè)函數(shù)(或者類)作為參數(shù)吃警,返回值也是也是一個(gè)函數(shù)(或者類)

無參裝飾器

無參裝飾器就是不接受參數(shù)的裝飾器糕篇,無參裝飾器嵌套了兩層函數(shù),一個(gè)是外部函數(shù)接受參數(shù)酌心,一個(gè)是內(nèi)部的返回函數(shù)拌消。

import time
def timeit(func):
    def wrapper():  # wrapper是返回函數(shù),被裝飾的函數(shù)的函數(shù)名接受這個(gè)函數(shù)安券,相當(dāng)于f = wrapper()
        start = time.clock()
        func()  # func就是被傳入裝飾器的函數(shù)墩崩,func()在wrapper()內(nèi),在調(diào)用wrapper時(shí)侯勉,會(huì)調(diào)用func()
        end = time.clock()
        print('Used {}'.format(end - start))
    return wrapper
@timeit
def f():
    print('In f()')
f()  # 此時(shí)f = wrapper
In f()
Used 0.00031399999999992545

1.首先鹦筹,把foo函數(shù)當(dāng)做參數(shù)傳入timeit
2.foo接受返回的函數(shù)wrapper,此時(shí)的foo指向wrapper址貌,執(zhí)行foo其實(shí)執(zhí)行wrapper
3.調(diào)用foo其實(shí)調(diào)用了wrapper铐拐,在調(diào)用wrapper時(shí)又重新調(diào)用了原本的foo函數(shù),在調(diào)用wrapper其實(shí)調(diào)用foo

多裝飾器

def deco1(func):
    def wrapper():
        print('In Deco1')
        func()
    return wrapper
def deco2(func):
    def wrapper():
        print('In Deco2')
        func()
    return wrapper
@deco2
@deco1
def foo():
    print('In foo()')
foo()
In Deco2
In Deco1
In foo()

等價(jià)與:

foo = deco2(deco1(foo))

在多裝飾器中练对,緊挨著被裝飾函數(shù)的裝飾器屬于最內(nèi)層遍蟋,最先調(diào)用,最外層的裝飾器最后調(diào)用螟凭。

有參裝飾器

def make_header(level):  #接受參數(shù)
    print('Create decorator')
    def decorator(func):  #接受函數(shù)
        print('Initialize...')
        def wrapper():
            print('Call')
            return '<h{0}>{1}</h{0}>'.format(level, func())
        return wrapper
    return decorator
@make_header(2)
def get_content():
    return 'hello world'
Create decorator
Initialize...
get_content()
Call





'<h2>hello world</h2>'

等價(jià)于:

get_content = make_header(2)
get_content = decorator(get_content)

def log(prefix):  # 接受裝飾器的參數(shù)
    def log_decorator(f):  # 內(nèi)部定義的wapper負(fù)責(zé)輸出log的參數(shù)+被裝飾函數(shù)名稱虚青, 接受被裝飾函數(shù)
        def wrapper(*args, **kw):   # 接受被裝飾函數(shù)的參數(shù)
                # 將log函數(shù)的參數(shù)引用
            print('[%s] in decorate wrapper s%s()...' % (prefix, f.__name__))
            f(*args, **kw)
        return wrapper  # 返回內(nèi)部函數(shù)給裝飾器
    return log_decorator  # 返回裝飾器給log函數(shù)
@log('DEBUG')
def test():
    print('out decorate run test()')


print(test())
[DEBUG] in decorate wrapper stest()...
out decorate run test()
None

有參裝飾器嵌套了三層函數(shù),最外層的函數(shù)接受裝飾器的參數(shù)螺男,中間的函數(shù)接受被裝飾函數(shù)棒厘,最內(nèi)層的函數(shù)接受被裝飾函數(shù)的參數(shù)钟些。

import time
from functools import reduce

def performance(unit):
    def perf_decorator(f):
        def wrapper(*args, **kargs):
            start_time = time.time()
            run_func = f(*args, **kargs)
            end_time = time.time()
            run_time = (end_time - start_time) * 1000 if unit == 'ms' else (end_time - start_time)
            print('call %s() in %f %s' % (f.__name__, run_time, unit))
            return run_func
        return wrapper
    return perf_decorator
@performance('ms')
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))
print(factorial(10))
call factorial() in 0.010252 ms
3628800

裝飾器的影響

def log(f):
    def wrapper(*args, **kw):
        print('call...')
        return f(*args, **kw)
    return wrapper
@log
def f2(x):
    pass
print(f2.__name__)
wrapper

由于decorator返回的新函數(shù)函數(shù)名已經(jīng)不是'f2',而是@log內(nèi)部定義的'wrapper'绊谭。 這對(duì)于那些依賴函數(shù)名的代碼就會(huì)失效政恍。decorator還改變了函數(shù)的__doc__等其它屬性。

改善:

import functools
def log(f):
    @functools.wraps(f)
    def wrapper(*args, **kw):
        print('call...')
        return f(*args, **kw)
    return wrapper

最后需要指出达传,由于我們把原函數(shù)簽名改成了(*args, **kw)篙耗,因此,無法獲得原函數(shù)的原始參數(shù)信息宪赶。 即便我們采用固定參數(shù)來裝飾只有一個(gè)參數(shù)的函數(shù)

def log(f):
    @functools.wraps(f)
    def wrapper(x):
        print('call...')
        return f(x)
    return wrapper

也可能改變?cè)瘮?shù)的參數(shù)名宗弯,因?yàn)樾潞瘮?shù)的參數(shù)名始終是 'x',原函數(shù)定義的參數(shù)名不一定叫 'x'

import time
import functools

def performance(unit):
    def perf_decorator(f):
        @functools.wraps(f)
        def wrapper(*args, **kw):
            t1 = time.time()
            r = f(*args, **kw)
            t2 = time.time()
            t = (t2 - t1) * 1000 if unit == 'ms' else (t2 - t1)
            print('call %s() in %f %s' % (f.__name__, t, unit))
            return r
        return wrapper
    return perf_decorator
@performance('ms')
def factorial(n):
    return reduce(lambda x, y: x * y, range(1, n + 1))
print(factorial(10))
call factorial() in 0.008821 ms
3628800
print(factorial.__name__)
factorial

基于類的裝飾器

可以定義基于類的裝飾器

class Bold(object):
    def __init__(self, func):
        self.func = func
 
    def __call__(self, *args, **kwargs):
        return '<b>' + self.func(*args, **kwargs) + '</b>'
@Bold
def hello(name):
    return 'hello %s' % name
hello('world')
'<b>hello world</b>'
  • __init__():它接收一個(gè)函數(shù)作為參數(shù)搂妻,也就是被裝飾的函數(shù)
  • __call__():讓類對(duì)象可調(diào)用蒙保,就像函數(shù)調(diào)用一樣,在調(diào)用被裝飾函數(shù)時(shí)被調(diào)用

閱讀

Python Decorator 基礎(chǔ)
Python: 會(huì)打扮的裝飾器

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末欲主,一起剝皮案震驚了整個(gè)濱河市邓厕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌扁瓢,老刑警劉巖详恼,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異引几,居然都是意外死亡昧互,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門伟桅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敞掘,“玉大人,你說我怎么就攤上這事楣铁【裂悖” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵民褂,是天一觀的道長(zhǎng)茄菊。 經(jīng)常有香客問我,道長(zhǎng)赊堪,這世上最難降的妖魔是什么面殖? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮哭廉,結(jié)果婚禮上脊僚,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好辽幌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布增淹。 她就那樣靜靜地躺著,像睡著了一般乌企。 火紅的嫁衣襯著肌膚如雪虑润。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天加酵,我揣著相機(jī)與錄音拳喻,去河邊找鬼。 笑死猪腕,一個(gè)胖子當(dāng)著我的面吹牛冗澈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播陋葡,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼亚亲,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了腐缤?” 一聲冷哼從身側(cè)響起捌归,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎柴梆,沒想到半個(gè)月后陨溅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體终惑,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绍在,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雹有。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偿渡。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖霸奕,靈堂內(nèi)的尸體忽然破棺而出溜宽,到底是詐尸還是另有隱情,我是刑警寧澤质帅,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布适揉,位于F島的核電站,受9級(jí)特大地震影響煤惩,放射性物質(zhì)發(fā)生泄漏嫉嘀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一魄揉、第九天 我趴在偏房一處隱蔽的房頂上張望剪侮。 院中可真熱鬧,春花似錦洛退、人聲如沸瓣俯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽彩匕。三九已至腔剂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驼仪,已是汗流浹背桶蝎。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谅畅,地道東北人登渣。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像毡泻,于是被迫代替她去往敵國(guó)和親胜茧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 每個(gè)人都有的內(nèi)褲主要功能是用來遮羞仇味,但是到了冬天它沒法為我們防風(fēng)御寒呻顽,咋辦?我們想到的一個(gè)辦法就是把內(nèi)褲改造一下丹墨,...
    chen_000閱讀 1,362評(píng)論 0 3
  • 呵呵廊遍!作為一名教python的老師,我發(fā)現(xiàn)學(xué)生們基本上一開始很難搞定python的裝飾器贩挣,也許因?yàn)檠b飾器確實(shí)很難懂...
    TypingQuietly閱讀 19,551評(píng)論 26 186
  • 原文出處: dzone 譯文出處:Wu Cheng(@nullRef) 1. 函數(shù) 在python中喉前,函數(shù)通過...
    DraculaWong閱讀 521評(píng)論 0 3
  • Python 裝飾器(decorator)是一種語法糖(Syntactic sugar), 以上代碼和以下代碼是等...
    ButteredCat閱讀 753評(píng)論 0 0
  • 01 稀缺王财,簡(jiǎn)單點(diǎn)說就是實(shí)際擁有的滿足不了內(nèi)心的需要卵迂,需求大于供給。 物質(zhì)绒净、時(shí)間见咒、情緒都可能出現(xiàn)稀缺的情況。 世間...
    嘉文莫爾閱讀 588評(píng)論 0 0