python 装饰器

回顾了廖老师的装饰器的教程,记录一下
本质上,装饰器就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
    
@log
def now():
    print('2015-3-25')
    
now()
call now():
2015-3-25

@log放到now()函数的定义处,相当于执行了语句:now = log(now)
由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

下面我们根据几个需求好好理解一下上述的执行流程。假设我们要打印一个函数的执行时间

def metric(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args,**kwargs):
            t1 = time.time()
            res = func(*args,**kwargs)
            t2 = time.time()
            print('%s executed in %s ms %s' % (func.__name__, t2-t1, text))
            return res
        return wrapper
    return decorator

@metric
def fast(x, y, z):
    time.sleep(0.0012)
    return x + y;

执行上述函数,会发现报错decorator() takes 1 positional argument but 3 were given',这是因为带参数的装饰器执行流程为metric(text)(func)(*args,**kwargs),当没有传入text,metric的第一个参数变为func,decorator的参数变为(*args,**kwargs),即fast的参数,所以会报以上错误。
如何解决呢?我们加一个判断如下

def metric(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args,**kwargs):
            t1 = time.time()
            res = func(*args,**kwargs)
            t2 = time.time()
            print('%s executed in %s ms %s' % (func.__name__, t2-t1, text))
            return res
        return wrapper
    
    if type(text)==str:
        return decorator
    else:
        return decorator(text)
        
@metric
def fast(x, y, z):
    time.sleep(0.0012)
    return x + y

这样就可以满足上述不加text的情况,如果还想这样调用metric(),可以设定一个默认值

def metric(text=""):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args,**kwargs):
            t1 = time.time()
            res = func(*args,**kwargs)
            t2 = time.time()
            if text:
                print('%s executed in %s ms %s' % (func.__name__, t2-t1, text))
            else:
                print('%s executed in %s ms' % (func.__name__, t2-t1))
            return res
        return wrapper
    
    if type(text)==str:
        return decorator
    else:
        return decorator(text)

@metric()
def fast(x, y, z):
    time.sleep(0.0012)
    return x + y

总结一下,关键是理解函数传入的流程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值