python 装饰器常见用法

跟踪调用

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

计时调用

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)

跟踪对象接口

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]

管理函数和类

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:
    
    ERROR:root:no attribute registered!
    None
    ERROR:root:no attribute registered!
None

验证函数参数

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

参数类型检测

def typetest(**argchecks):
    def onDecorator(func):
            code = func.__code__ # __code__ 返回已编译的函数对象
            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():
                    # 检测参数是否在关键字参数中
                    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)
                    # 检测参数是否在位置参数中
                    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 
        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  ,not the expected type 

你可能感兴趣的:(python 装饰器常见用法)