一个简单场景:我有一个给函数计时的装饰器,现在我想给函数运行的时间添加一个时间偏移量time_lag,该时间偏移量不是常数,想通过把该参数传入装饰器中来实现。

  • 实现方式1:
import time
from functools import wraps
def timer_func(func,time_lag):
    @wraps(func)
    def wer(*args,**kwargs):
        t1=time.time()
        r=func(*args,**kwargs)
        t2=time.time()
        cost=t2-t1+time_lag
        print('time cost %s'%cost)
        return r
    return wer

def func(n:int):
    while n>0:
        n=n-1
    return n
a=timer_func(func,time_lag=10)
a(1000000)

但是这样的实现方式,就无法语法糖@,每次都要通过函数调用来实现,就失去了装饰器简明、方便的特点了。

因此用另一种实现方式,只需要在原有装饰函数外面再嵌套一层外函数,将想传入装饰器的函数传入最外层函数即可,实现方式如下:

  • 实现方式2:
from functools import wraps
import time
def timer(time_lag):

    def decorator(func):
        @wraps(func)
        def wer(*args, **kwargs):
            t1 = time.time()
            r = func(*args, **kwargs)
            t2 = time.time()
            cost = t2 - t1
            res=cost+time_lag
            print('time cost %s' % res)
            return func(*args, **kwargs)#返回函数本身结果
        return wer#返回内函数
    return decorator#返回外函数

def func(n:int):
    '''
    this is a func test
    :param n:
    :return:
    '''
    while n>0:
        n=n-1
    return n

>>>a=timer(10)#返回外函数,#传入time_lag=10
>>>print(a)
<function timer.<locals>.decorator at 0x000002027D8E5E50>
>>>b=a(func)#返回内函数
>>>print(b)
<function func at 0x000002027D9434C0>
>>>c=b(999999)#调用内函数返回结果
>>>print(c)
time cost 10.069796323776245
0

上述逐步调用的过程只是为了清晰的展示,该函数看起来复杂,实际上本质思想还是闭包的思想,只是在装饰器外再嵌套了一层外函数。每个外函数返回的是它的下一级内函数。最内层函数返回的是调用函数的结果。该方式还可以通过语法糖简明的调用:

@timer(10)#传入time_lag=10
def func(n:int):

    while n>0:
        n=n-1
    return n
>>>func(99999)
time cost 10.008002042770386

@timer(20)#传入time_lag=20
def func1(n:int):

    while n>0:
        n=n-1
    return n
>>>func1(22222222)
time cost 21.447203874588013
  • 创建一个带参数的日志装饰器

创建一个日志装饰器,该日志的输出等级、名称、信息通过传参传入装饰器中

def logged(level,name=None,message=None):

    def decorator(func):
        logname=name if name else func.__module__
        log=logging.getLogger(logname)
        logmsg=message if message else func.__name__

        @wraps(func)
        def wraper(*args,**kwargs):
            log.log(level,message)
            return func(*args,**kwargs)
        return wraper
    return decorator

@logged(logging.DEBUG,'test_name')
def func(a):
    return a+1

千翻娃儿
4 声望5 粉丝