python 装饰器(decorator)用法小结

python的代码的特点:优雅,明确,简单,装饰器就是这些特点的很好体现。

一、装饰器的本质

python装饰器本质:func=wrapper(func)  <==>  @wrapper 两种等价的写法,可以看出decorator就是一个返回函数的高阶函数,decorator的作用在于它可以在不用更改原函数的情况下增加新的功能,为已经存在的函数或对象添加额外的功能,也可以抽离大量与函数功能本身无关的相似代码进行复用,常用于事物处理,插入日志等场景

完整的装饰器写法如下:log装饰器,包含@语法糖,可用于任意目标函数

import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

带参数的decorator如下: 

import functools

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

下面我们来看看装饰器到底是什么 

1、要了解装饰器首先要理解,函数本身也是一个对象,函数对象也可以被赋值,所以函数也能通过变量来被调用。eg:

#coding:utf-8
import time
def func():
    print time.strftime('%Y.%m.%d',time.localtime(time.time()))  
    
if __name__=='__main__':
    func()
    f=func
    f() #通过变量调用函数
    print func.__name__
    print f.__name__
2019.02.20
2019.02.20
func
func

2、设想func()函数本身不能满足功能要求,代码本身又不能做更改,比如上面的代码,笔者不止想让它打印日期,还想加上程序运行时间,这里就用到了装饰器。参照上面的标准写法,demo就是一个装饰器,包含@语法糖,增加了打印func()运行时间的功能

#coding:utf-8
import time
import functools

def demo(func):
    def wrapper():
        starttime=time.time()
        func()
        endtime=time.time()
        ms=(endtime-starttime)*1000
        print ("Running time: %d ms" %ms)
    return wrapper
@demo        
def func():
    time.sleep(1)
    print time.strftime('%Y.%m.%d',time.localtime(time.time()))  

if __name__=='__main__':
    func()
    print func.__name__
    
2019.02.20
Running time: 1000 ms
wrapper

上述代码func函数被wrapper包装在里面,相当于func=demo(func),由函数结构看出demo(func)返回的值是wrapper,即func()=demo(func)()=wrapper()

注意: 从结果看出函数对象的__name__属性变了,从func变成wrapper,因为demo函数中定义返回的就是wrapper

3、在实际应用过程中,函数是有参数的,而且参数个数类型不定,装饰器也能搞定

固定参数的装饰器示例代码如下:

#coding:utf-8
import time,functools

def demo(func):
    def wrapper(a,b):
        starttime=time.time()
        func(a,b)
        endtime=time.time()
        ms=(endtime-starttime)*1000
        print ("Running time: %d ms" %ms)
    return wrapper
@demo        
def func(a,b):
    time.sleep(1)
    print time.strftime('%Y.%m.%d',time.localtime(time.time()))  
    print a+b

if __name__=='__main__':
    func(1,3)
    print func.__name__
    
2019.02.20
4
Running time: 1000 ms
wrapper

 参数不定的装饰器(可用于任意目标函数)示例代码如下:

 可变参数*args和关键字参数**kw知识点传送门—https://blog.youkuaiyun.com/weixin_43533825/article/details/87517242

#coding:utf-8
import time,functools

def demo(func):
    def wrapper(*args,**kw):
        starttime=time.time()
        func(*args,**kw)
        endtime=time.time()
        ms=(endtime-starttime)*1000
        print ("Running time: %d ms" %ms)
    return wrapper
@demo        
def func(a,b):
    time.sleep(1)
    print time.strftime('%Y.%m.%d',time.localtime(time.time()))  
    print a+b
    
@demo
def functest(a,b,c=2):
    time.sleep(1)
    print time.strftime('%Y.%m.%d',time.localtime(time.time()))  
    print a+b+c

if __name__=='__main__':
    func(1,3)
    functest(1,2,c=10)
    print func.__name__
    
2019.02.20
4
Running time: 1000 ms
2019.02.20
13
Running time: 1000 ms
wrapper

 上面提到函数被“装饰”之后,__name__属性就变了,这样会导致依赖函数签名的代码执行报错,python内置的functools.wraps解决了这个问题,示例代码如下:可以看到函数的__name__属性还是func。

#coding:utf-8
import time,functools

def demo(func):
    @functools.wraps(func)
    def wrapper(*args,**kw):
        starttime=time.time()
        func(*args,**kw)
        endtime=time.time()
        ms=(endtime-starttime)*1000
        print ("Running time: %d ms" %ms)
    return wrapper
@demo        
def func(a,b):
    time.sleep(1)
    print time.strftime('%Y.%m.%d',time.localtime(time.time()))  
    print a+b
    
@demo
def functest(a,b,c=2):
    time.sleep(1)
    print time.strftime('%Y.%m.%d',time.localtime(time.time()))  
    print a+b+c

if __name__=='__main__':
    func(1,3)
    functest(1,2,c=10)
    print func.__name__
    
2019.02.20
4
Running time: 1000 ms
2019.02.20
13
Running time: 1000 ms
func

 二、多装饰器

原函数需要增加多个功能,因此可能需要多个装饰器,示例代码如下:

多个装饰器执行顺序:由近及远。

#coding:utf-8
import time,functools

def demo1(func):
    print "demo1 run"
    def wrapper(*args,**kw):
        print "demo1 before func"
        func(*args,**kw)
        print "demo1 after func"
    return wrapper

def demo2(func):
    print "demo2 run"
    def wrapper(*args,**kw):
        print "demo2 before func"
        func(*args,**kw)
        print "demo2 after func"
    return wrapper

@demo1
@demo2        
def func(a,b):
    time.sleep(1)
    print time.strftime('%Y.%m.%d',time.localtime(time.time()))  
    print a+b
    

if __name__=='__main__':
    func(1,3)
    print func.__name__
    
demo2 run
demo1 run
demo1 before func
demo2 before func
2019.02.20
4
demo2 after func
demo1 after func
wrapper

 三、带参数的装饰器

实际应用场景中,decorator本身也会需要传入参数(eg:@log会需要传入日志级别),实际上是三层嵌套,示例代码如下:

#coding:utf-8
import time,functools

def log(level):
    def demo(func):
        @functools.wraps(func)
        def wrapper(*args,**kw):
            starttime=time.time()
            print level
            func(*args,**kw)
            endtime=time.time()
            ms=(endtime-starttime)*1000
            print ("Running time: %d ms" %ms)
        return wrapper
    return demo

@log('Info')        
def func(a,b):
    time.sleep(1)
    print time.strftime('%Y.%m.%d',time.localtime(time.time()))  
    print a+b
    

if __name__=='__main__':
    func(1,3)
    print func.__name__
    
Info
2019.02.21
4
Running time: 1002 ms
func

 和上面的func=demo(func)相比,这里相当于func=log(level)(func)

func()=log(level='Info')(func)=demo(level)(func)()=wrapper(level)()

四、类装饰器

由前面的装饰器本质可知,如果类对象要实现装饰器,它必须是可调用的;而__call__()这个方法,可以将类的实例变成可调用对象,通过__init__()传入函数,重载__call__()函数,达到装饰器的目的,示例代码如下:

class log(object):
    def __init__(self, func):
        self.func = func
    def __call__(self,*args,**kw):
        print("decorator start")
        self.func(*args,**kw)
        print("decorator end")

@log
def func(a,b,c=10):
    print a+b+c

func(1,2,5)
decorator start
8
decorator end

带参数的类装饰器,__init__()传入的不是函数,而是参数,__call__()接受一个函数,并返回一个函数,示例代码如下:

class log(object):
    def __init__(self, level='info'):
        self.level = level
    def __call__(self,func):
        def wrapper(*args,**kw):
            print("decorator start")
            level=self.level
            print level
            func(*args,**kw)
            print("decorator end")
        return wrapper

@log(level='warnning')
def funcsss(a,b,c=10):
    print a+b+c

funcsss(1,2,5)
decorator start
warnning
8
decorator end

小结:装饰器是对原函数、对象的重新封装,对函数功能的额外加强。装饰器除了本文提到的,还有内置装饰器等等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值