Python decorator裝飾器

文中知識(shí)點(diǎn)和代碼示例學(xué)習(xí)自慕課網(wǎng),python進(jìn)階部分(http://www.imooc.com/learn/317) .學(xué)習(xí)筆記

裝飾器的理解

裝飾器本質(zhì)上是一個(gè)高階函數(shù),接受一個(gè)函數(shù),進(jìn)行處理,然后返回一個(gè)新的函數(shù)。

# 有一個(gè)簡單的函數(shù)
def f1(x):
    return x * 2
print f1(5)

#輸出:10

# 實(shí)現(xiàn)運(yùn)行該函數(shù)時(shí),輸出該函數(shù)的名稱的日志功能
# 法一:直接在函數(shù)中寫出,好理解,但是如果函數(shù)很多,那每個(gè)函數(shù)都要加一遍
def f1(x):
    print "call " + f1.__name__ + "().."
    return x*2
print f1(5)

#輸出:
#call f1()..
#10

# 法二:使用裝飾器,創(chuàng)建裝飾器函數(shù)
def flog(f):        #定義裝飾器函數(shù),接受參數(shù)是 f 函數(shù)
    def fn(x):      #定義新的函數(shù),來處理 f 函數(shù),添加我們需要的日志信息,并返回 f 函數(shù),參數(shù) x 是 f1 的參數(shù),如果 f1 函數(shù)有多個(gè),這邊也要寫多個(gè),要讓 @log 自適應(yīng)任何參數(shù)定義的函數(shù),可以利用Python的 *args 和 **kw,保證任意個(gè)數(shù)的參數(shù)總是能正常調(diào)用
        print "call " + f.__name__ + "().." 
        return f(x) #執(zhí)行原 f 函數(shù)
    return fn       #返回新的函數(shù)

# 調(diào)用裝飾器,效果和 g1 = flog(f1);print g1(5) 一致。
@flog               
def f1(x):
    return x * 2
print f1(5)

#輸出:
#call f1()..
#10

注: 上邊f1(x)只接受一個(gè)函數(shù),如果接受兩個(gè)函數(shù)就會(huì)報(bào)錯(cuò),要讓 @flog 自適應(yīng)任何參數(shù)定義的函數(shù),可以利用Python的 *args**kw,保證任意個(gè)數(shù)的參數(shù)總是能正常調(diào)用

例:計(jì)算函數(shù)調(diào)用的時(shí)間可以記錄調(diào)用前后的當(dāng)前時(shí)間戳,然后計(jì)算兩個(gè)時(shí)間戳的差。

import time

# 定義裝飾器
def performance(f):
    def fn(*args, **kw):    #可以接受任意參數(shù)
        t1 = time.time()    #記錄執(zhí)行前的時(shí)間
        r = f(*args, **kw)  #執(zhí)行原函數(shù),并保存執(zhí)行結(jié)果到變量 r 中
        t2 = time.time()    #記錄執(zhí)行結(jié)束后的時(shí)間
        print 'call %s() in %fs' % (f.__name__, (t2 - t1))  #添加自定義輸出內(nèi)容
        return r            #返回原函數(shù)的執(zhí)行結(jié)果
    return fn               #返回新函數(shù)

#調(diào)用裝飾器
@performance
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)

帶參數(shù)的裝飾器

上邊的實(shí)現(xiàn)的裝飾器函數(shù),只能輸出固定的內(nèi)容,除了f.__name__所定義的函數(shù)名稱。如果想要根據(jù)函數(shù)的不同來給輸出的日志劃分等級(jí)的,如 a 函數(shù)日志等級(jí)為info,b 函數(shù)日志等級(jí)為debug,這里需要用到帶參數(shù)的裝飾器。如:

@log('DEBUG')
def my_func():
    pass

@log('DEBUG')等于之前無參數(shù)的裝飾器的@log,即@log('DEBUG')這個(gè)返回的函數(shù)相當(dāng)于之前無參數(shù)裝飾器的@log。所以要在無參數(shù)的裝飾器上層再加一個(gè)函數(shù)的嵌套。

例:輸出日志等級(jí)

#coding=utf-8

"""
    帶參數(shù)的裝飾器,寫三層嵌套的函數(shù)
"""

def flog(devel):    # 定義帶參數(shù)的裝飾器.
    def log_decorator(f):   #和無參數(shù)的裝飾器相同
        def add_self(*args,**kwargs):   #添加輸出的日志,執(zhí)行f函數(shù)
            print "[%s],call %s().." % (devel,f.__name__)
            return  f(*args,**kwargs)
        return add_self     
    return log_decorator    #返回給flog("INFO")

@flog("INFO")
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)

"""
輸出
[INFO],call factorial()..
3628800
"""

例2:上一節(jié)的@performance只能打印秒,請(qǐng)給@performace增加一個(gè)參數(shù),允許傳入's''ms'

import time
def performance(unit):s
    def perf_decorator(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)

完善裝飾器

經(jīng)過@decorator“改造”后的函數(shù),和原函數(shù)相比會(huì)有不同的地方,如:

# 在沒有decorator的情況下,打印函數(shù)名:
def f1(x):
    pass
print f1.__name__

#輸出:f1

# 有decorator的情況下,再打印函數(shù)名:
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__等其它屬性。如果要讓調(diào)用者看不出一個(gè)函數(shù)經(jīng)過了@decorator的“改造”,就需要把原函數(shù)的一些屬性復(fù)制到新函數(shù)中。

Python內(nèi)置的functools可以用來自動(dòng)化完成這個(gè)“復(fù)制”的任務(wù):

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ù)

例:該方法使用到上節(jié)的例子中

import time, 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.__name__
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 前言 Python的修飾器的英文名叫Decorator,當(dāng)你看到這個(gè)英文名的時(shí)候,你可能會(huì)把其跟Design Pa...
    linheimx閱讀 659評(píng)論 0 4
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,569評(píng)論 19 139
  • 要點(diǎn): 函數(shù)式編程:注意不是“函數(shù)編程”,多了一個(gè)“式” 模塊:如何使用模塊 面向?qū)ο缶幊蹋好嫦驅(qū)ο蟮母拍?、屬性?..
    victorsungo閱讀 1,697評(píng)論 0 6
  • 裝飾器 裝飾器本質(zhì)上是一個(gè)Python函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動(dòng)的前提下增加額外功能,裝飾器的返...
    時(shí)間之友閱讀 2,447評(píng)論 0 3
  • 美國作家杜魯門·卡波蒂在接受采訪時(shí)曾說: 我總是會(huì)有幻覺,認(rèn)為故事的開端、中間和結(jié)尾,整出戲都在我頭腦里同時(shí)上演—...
    兔U閱讀 2,107評(píng)論 5 17

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