python 裝飾器常見用法

跟蹤調(diào)用

class tracer: # State via instance attributes
    def __init__(self, func): # On @ decorator
        self.calls = 0 # Save func for later call
        self.func = func
    def __call__(self, *args, **kwargs): # On call to original function
        self.calls += 1
        print('call %s to %s' % (self.calls, self.func.__name__))
        return self.func(*args, **kwargs)

用例:

@tracer
def spam(a, b, c): # Same as: spam = tracer(spam)
    print(a + b + c) # Triggers tracer.__init__

@tracer
def eggs(x, y): # Same as: eggs = tracer(eggs)
    print(x ** y) # Wraps eggs in a tracer object

spam(1, 2, 3) # Really calls tracer instance: runs tracer.__call__
spam(a=4, b=5, c=6) # spam is an instance attribute
eggs(2, 16) # Really calls tracer instance, self.func is eggs
eggs(4, y=4) # self.calls is per-function here (need 3.0 nonlocal)

output:
    call 1 to spam
    6
    call 2 to spam
    15
    call 1 to eggs
    65536
    call 2 to eggs
256

計(jì)時(shí)調(diào)用

import time

def timer(label='', trace=True): # On decorator args: retain args
    class Timer:
        def __init__(self, func): # On @: retain decorated func
            self.func = func
            self.alltime = 0
        def __call__(self, *args, **kargs): # On calls: call original
            start = time.clock()
            result = self.func(*args, **kargs)
            elapsed = time.clock() - start
            self.alltime += elapsed
            if trace:
                format = '%s %s: %.5f, %.5f'
                values = (label, self.func.__name__, elapsed, self.alltime)
                print(format % values)
                return result
    return Timer

用例:

@timer(label='[CCC]==>')
def listcomp(N): # Like listcomp = timer(...)(listcomp)
    return [x * 2 for x in range(N)] # listcomp(...) triggers Timer.__call__

@timer(trace=True, label='[MMM]==>')
def mapcall(N):
    return map((lambda x: x * 2), range(N))
for func in (listcomp, mapcall):
    print('')
    result = func(5) # Time for this call, all calls, return value
    func(50000)
    func(500000)
    func(1000000)
    print(result)
    print('allTime = %s' % func.alltime) # Total time for all calls
print('map/comp = %s' % round(mapcall.alltime / listcomp.alltime, 3))

output:
    [CCC]==> listcomp: 0.00003, 0.00003
    [CCC]==> listcomp: 0.00640, 0.00643
    [CCC]==> listcomp: 0.08687, 0.09330
    [CCC]==> listcomp: 0.17911, 0.27241
    allTime = 0.272407666337
    [MMM]==> mapcall: 0.00004, 0.00004
    [MMM]==> mapcall: 0.01340, 0.01343
    [MMM]==> mapcall: 0.13907, 0.15250
    [MMM]==> mapcall: 0.27907, 0.43157
    [0, 2, 4, 6, 8]
    allTime = 0.431572169089
    map/comp = 1.584
[0, 2, 4, 6, 8]

單例

class singleton:
    def __init__(self, aClass): # On @ decoration
        self.aClass = aClass
        self.instance = None
    def __call__(self, *args): # On instance creation
        if self.instance == None:
            self.instance = self.aClass(*args) # One instance per class
    return self.instance

用例:

@singleton
class Test:
    def __init__(self,**args):
        self.a = args["a"]
        self.b = args["b"]
def main():
    t1 = Test(**{"a":1,"b":2})
    print("a:",t1.a,"b:",t1.b)
    t2 = Test()
    print("a:",t2.a,"b:",t2.b)
   
output:
    ('a:', 1, 'b:', 2)
    ('a:', 1, 'b:', 2)

跟蹤對(duì)象接口

def Tracer(aClass): # On @ decorator
    class Wrapper:
        def __init__(self, *args, **kargs): # On instance creation
            self.fetches = 0
            self.wrapped = aClass(*args, **kargs) # Use enclosing scope name
        def __getattr__(self, attrname):
            print('Trace: ' + attrname) # Catches all but own attrs
            self.fetches += 1
            return getattr(self.wrapped, attrname) # Delegate to wrapped obj
    return Wrapper

用例:

@Tracer
class Spam: # Spam = Tracer(Spam)
    def display(self): # Spam is rebound to Wrapper
    print('Spam!' * 8)

@Tracer
class Person: # Person = Tracer(Person)
    def __init__(self, name, hours, rate): # Wrapper remembers Person
        self.name = name
        self.hours = hours
        self.rate = rate
    def pay(self): # Accesses outside class traced
        return self.hours * self.rate # In-method accesses not traced

food = Spam() # Triggers Wrapper()
food.display() # Triggers __getattr__
print([food.fetches])
bob = Person('Bob', 40, 50) # bob is really a Wrapper
print(bob.name) # Wrapper embeds a Person
print(bob.pay())
print('')
sue = Person('Sue', rate=100, hours=60) # sue is a different Wrapper
print(sue.name) # with a different Person
print(sue.pay())
print(bob.name) # bob has different state
print(bob.pay())
print([bob.fetches, sue.fetches]) # Wrapper attrs not traced

output:
    Trace: display
    Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!
    [1]
    Trace: name
    Bob
    Trace: pay
    2000
    Trace: name
    Sue
    Trace: pay
    6000
    Trace: name
    Bob
    Trace: pay
    2000
    [4, 2]

管理函數(shù)和類

def register(label="",registerd = False):
    def onRegister(func):
        key = label if label else func.__name__
        if registerd:
            registerMap[key] = func
        return func
    return onRegister

用例:

@register(registerd=True)
def test1():
    print("test1")

@register(label="modify")
def test2():
    print("test2")

@register(label="test1",registerd=True)
def test3():
    print("test3")

print getInstance("test1")
print getInstance("test2")
print getInstance("modify")

output:
    <function test3 at 0x00000000024C1DD8>
    ERROR:root:no attribute registered!
    None
    ERROR:root:no attribute registered!
None

驗(yàn)證函數(shù)參數(shù)

def rangetest(**argchecks): 
        def onDecorator(func): 
                code = func.__code__
                allargs = code.co_varnames[:code.co_argcount]
                funcname = func.__name__
                def onCall(*pargs, **kargs):
                    positionals = list(allargs)
                    positionals = positionals[:len(pargs)]
                    for (argname, (low, high)) in argchecks.items():
                        # For all args to be checked
                        if argname in kargs:
                            # Was passed by name
                            if kargs[argname] < low or kargs[argname] > high:
                                errmsg = '{0} argument "{1}" not in {2}..{3}'
                                errmsg = errmsg.format(funcname, argname, low, high)
                                raise TypeError(errmsg)
                            elif argname in positionals:
                                # Was passed by position
                                position = positionals.index(argname)
                                if pargs[position] < low or pargs[position] > high:
                                    errmsg = '{0} argument "{1}" not in {2}..{3}'
                                    errmsg = errmsg.format(funcname, argname, low, high)
                                    raise TypeError(errmsg)
                            else:
                                # Assume not passed: default
                                if True:
                                    print('Argument "{0}" defaulted'.format(argname))
                            return func(*pargs, **kargs) # OK: run original call
                return onCall
        return onDecorator

用例:

class Person:
    def __init__(self, name, job, pay):
        self.job = job
        self.pay = pay
    # giveRaise = rangetest(...)(giveRaise)
    @rangetest(percent=(0.0, 1.0)) # percent passed by name or position
    def giveRaise(self, percent):
        self.pay = int(self.pay * (1 + percent))

bob = Person('Bob Smith', 'dev', 100000)
sue = Person('Sue Jones', 'dev', 100000)
bob.giveRaise(.10)
sue.giveRaise(percent=.20)
print(bob.pay, sue.pay)
bob.giveRaise(1.10)
bob.giveRaise(percent=1.20)
# Test omitted defaults: skipped

output:
    Argument "percent" defaulted
    (100000, 120000)
    Traceback (most recent call last):
      File "d:/huyaowen/workspace/demo/rangetest.py", line 54, in <module>
        main()
      File "d:/huyaowen/workspace/demo/rangetest.py", line 50, in main
        bob.giveRaise(percent=1.20)
      File "d:/huyaowen/workspace/demo/rangetest.py", line 18, in onCall
        raise TypeError(errmsg)
    TypeError: giveRaise argument "percent" not in 0.0..1.0

參數(shù)類型檢測

def typetest(**argchecks):
    def onDecorator(func):
            code = func.__code__ # __code__ 返回已編譯的函數(shù)對(duì)象
            allargs = code.co_varnames[:code.co_argcount]
            funcname = func.__name__
            def onCall(*pargs, **kargs):
                positionals = list(allargs)[:len(pargs)]
                for (argname, expectedtype) in argchecks.items():
                    # 檢測參數(shù)是否在關(guān)鍵字參數(shù)中
                    if argname in kargs:
                        if not isinstance(kargs[argname], expectedtype):
                            errmsg = '{0} argument "{1}" type is {2} ,not the expected type {3}'
                            errmsg = errmsg.format(funcname, argname, type(kargs[argname]), expectedtype)
                            raise TypeError(errmsg)
                    # 檢測參數(shù)是否在位置參數(shù)中
                    elif argname in positionals:
                        position = positionals.index(argname)
                        if not isinstance(pargs[position], expectedtype):
                            errmsg = '{0} argument "{1}" type is {2} ,not the expected type {3}'
                            errmsg = errmsg.format(funcname, argname, type(pargs[position]), expectedtype)
                            raise TypeError(errmsg)
                    else:
                        pass
                return func(*pargs, **kargs)
            return onCall
    return onDecorator

用例:

@typetest(a=int, c=float)
def func(a, b, c, d):
    print(a,b,c,d)

func(1, 2, 3.0, 4) # Okay
func('spam', 2, 99, 4)

output:
    (1, 2, 3.0, 4)
    Traceback (most recent call last):
      File "d:/huyaowen/workspace/demo/typetest.py", line 38, in <module>
        main()
      File "d:/huyaowen/workspace/demo/typetest.py", line 35, in main
        func('spam', 2, 99, 4)
      File "d:/huyaowen/workspace/demo/typetest.py", line 22, in onCall
        raise TypeError(errmsg)
    TypeError: func argument "a" type is <type 'str'> ,not the expected type <type 'int'>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末耗式,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子趁猴,更是在濱河造成了極大的恐慌刊咳,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,185評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件儡司,死亡現(xiàn)場離奇詭異娱挨,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)捕犬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,445評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門跷坝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酵镜,“玉大人,你說我怎么就攤上這事柴钻』淳拢” “怎么了?”我有些...
    開封第一講書人閱讀 157,684評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵贴届,是天一觀的道長缸濒。 經(jīng)常有香客問我,道長粱腻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,564評(píng)論 1 284
  • 正文 為了忘掉前任斩跌,我火速辦了婚禮绍些,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘耀鸦。我一直安慰自己柬批,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,681評(píng)論 6 386
  • 文/花漫 我一把揭開白布袖订。 她就那樣靜靜地躺著氮帐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪洛姑。 梳的紋絲不亂的頭發(fā)上上沐,一...
    開封第一講書人閱讀 49,874評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音楞艾,去河邊找鬼参咙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛硫眯,可吹牛的內(nèi)容都是我干的蕴侧。 我是一名探鬼主播,決...
    沈念sama閱讀 39,025評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼两入,長吁一口氣:“原來是場噩夢啊……” “哼净宵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起裹纳,我...
    開封第一講書人閱讀 37,761評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤择葡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后剃氧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刁岸,經(jīng)...
    沈念sama閱讀 44,217評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,545評(píng)論 2 327
  • 正文 我和宋清朗相戀三年她我,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了虹曙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片迫横。...
    茶點(diǎn)故事閱讀 38,694評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖酝碳,靈堂內(nèi)的尸體忽然破棺而出矾踱,到底是詐尸還是另有隱情,我是刑警寧澤疏哗,帶...
    沈念sama閱讀 34,351評(píng)論 4 332
  • 正文 年R本政府宣布呛讲,位于F島的核電站,受9級(jí)特大地震影響返奉,放射性物質(zhì)發(fā)生泄漏贝搁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,988評(píng)論 3 315
  • 文/蒙蒙 一芽偏、第九天 我趴在偏房一處隱蔽的房頂上張望雷逆。 院中可真熱鬧,春花似錦污尉、人聲如沸膀哲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,778評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽某宪。三九已至,卻和暖如春锐朴,著一層夾襖步出監(jiān)牢的瞬間兴喂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,007評(píng)論 1 266
  • 我被黑心中介騙來泰國打工焚志, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瞻想,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,427評(píng)論 2 360
  • 正文 我出身青樓娩嚼,卻偏偏與公主長得像蘑险,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子岳悟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,580評(píng)論 2 349

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