python装饰器(decorator)

什么是装饰器

装饰器本质上是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志,性能测试,事物处理,缓存,权限校验等场景。装饰器是解决这类为题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用,概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

首先使用闭包方式实现装饰器功能

>>> def test(func):
...     print("我是test函数")
...     def test_in():
...         print("我是test_in函数")
...         func()
...     return test_in
... 
>>> def foo():
...    print("foo....2222")
... 
>>> hah = test(foo)
我是test函数
>>> hah()
我是test_in函数
foo....2222

装饰器

def test(func):
    print("test函数")
    def test_in():
        print("test_in函数")
        func()
    return test_in
def foo():
    print("foo...222")

foo = test(foo)
foo()
变成装饰器
>>> def test(func):
...     print("test函数")
...     def test_in():
...         print("test_in函数")
...         func()
...     return test_in
... 
>>> @test
... def foo():
...     print("foo...222")
... 
test函数
>>> foo()
test_in函数
foo...222

需求:在已存在的多个基础平台的基础上添加用户验证功能

>>> def w1(func):
...     def inner():
...         print("验证一")
...         print("验证二")
...         print("验证三")
...         func()
...     return inner
... 
>>> @w1
... def f1():
...     print("f1")
... 
>>> @w1
... def f2():
...     print("f2")
... 
>>> @w1
... def f3():
...     print("f3")
... 
>>> @w1
... def f4():
...     print("f4")
... 
>>> f1()
验证一
验证二
验证三
f1
>>> f2()
验证一
验证二
验证三
f2
>>> f3()
验证一
验证二
验证三
f3
>>> f4()
验证一
验证二
验证三
f4
>>> 

代码执行过程
1.def w1(func): ==> 将w1函数加载到内存
2.@w1
我们可以看到解释器仅仅会解释这两句代码,因为函数在没有被调用之前其内部代码不会被执行,但是@w1这一句代码里却有大文章,@函数名,是python的一种语法糖
执行@w1函数,并将@w1下面的函数作为w1函数的参数,即:@w1等价与w1(f1)所以,内部会去执行:

def inner():
    #验证一
    #验证二
    #验证三
    f1()  #func是参数,此时func等于f1 return inner #返回的inner,inner代表的是函数,非执行函数,其实就是将原来的f1函数塞进另外一个函数中

w1的返回值
将执行完的w1函数返回值赋值给@w1下面的函数的函数名f1将w1的返回值再重新赋值给f1,即:

新 f1 = def inner():
        #验证一
        #验证二
        #验证三
        原来 f1()
    return inner

当我们想要执行f1函数时,就会执行新f1函数,在新f1函数内部先执行验证,再执行原来的f1函数,然后将原来f1函数的返回值返回给业务调用者。
如此一来,即执行了验证的功能,又执行了原来的f1函数的内容,并将原来f1函数返回值,返回给业务调用者。

>>> def w(func):
...     def inner():
...             func()
...     return inner
... 
>>> def f1():
...     print("f1....")
... 
>>> def f2():
...     print("f2....")
... 
>>> inner = w(f1)  #当调用inner()的时候本质是调用w函数内的inner函数,又因为inner函数内调用了f1,最终执行后调用了f1
>>> inner()
f1....

如果将inner名字修改成f1,那么和原来调用f1的方式就一样了。改写成装饰器

>>> def w(func):
...     def inner():
...             print("已经完成登录验证")
...             func()
...     return inner
... 
>>> @w
... def f1():
...     print("...f1...")
... 
>>> @w
... def f2():
...     print("...f2...")
... 
>>> f1()
已经完成登录验证
...f1...
>>> f2()
已经完成登录验证
...f2...
#w(f1)等价于@w def f1():print("...f1...")

装饰器的执行时机

>>> def test(func):
...     print("test内执行了...11")
...     def test_in():
...             print("test_in执行了...")
...             func()
...     print("test内执行了...22")
...     return test_in
... 
>>> @test #test这行python解释器执行就开始装饰了,把foo函数引用传入test,并且test方法内部被执行,并没有等foo被调用才执行
... def foo():
...     print("foo")
... 
test内执行了...11
test内执行了...22
>>> foo()
test_in执行了...
foo

装饰器(decorator)功能-统计方法的执行时间

>>> import time
>>> def w1(func):
...     def inner():
...             print("正在登录校验。。。")
...             start = time.time()
...             func()
...             end = time.time()
...             pass_ = end - start
...             print("执行:%s,花时间是:%d"%(func.__name__,pass_))
...     return inner
... 
>>> @w1
... def f1():
...     time.sleep(2)
...     print("f1....")
... 
>>> @w1
... def f2():
...     time.sleep(1)
...     print("f2...")
... 
>>> f1()
正在登录校验。。。
f1....
执行:f1,花时间是:2
>>> f2()
正在登录校验。。。
f2...
执行:f2,花时间是:1

装饰器示例
一,无参数的函数

>>> def func(func_name):
...     print("func....")
...     def func_in():
...             print("func_in....")
...             func_name()
...     return func_in
... 
>>> @func
... def test():
...     print("test....")
func....
>>> test()
func_in....
test....

test()
#上面代码可以理解为装饰器执行行为
#test = func(test)
#test先作为参数赋值给func_name后,test接收指向func返回的func_in
test()
#调用test(),即等价调用func_in()
#内部函数func_in被引用,所以外部函数的func_name变量(自由变量)并没有释放
#func_name里保存的是原test函数对象

二,被装饰的函数有参数

>>> def func(func_name):
...     def inner_fun(a,b):
...             print("%s在%s被调用"%(func_name.__name__,ctime()))
...             func_name(a,b)
...     return inner_fun
... 
>>> @func
... def test(a,b):
...     sleep(2)
...     print("test")
...     print(a+b)
... 
>>> test(10,20)
test在Thu Aug 30 20:59:22 2018被调用
test
30
说明:得到函数的名称和得到当前调用时间
func_name.__name__:得到当前函数的名称
ctime():得到当前的时间

三,被装饰的函数有不定长参数

>>> def func(func_name):
...     def inner_func(*args,**kwargs):
...             print("%s在%s被调用"%(func_name.__name__,ctime()))
...             func_name(*args,**kwargs)
...     return inner_func
... 
>>> @func
... def test(a,b,c,*args,**kwargs):
...     sleep(2)
...     print("test")
...     result = a + b + c
...     for i in args:
...             result += i
...     print(result)
... 
>>> test(10,20,30,40,66,77,88)
test在Fri Aug 31 09:17:11 2018被调用
test
331
>>> test(10,20,30)
test在Fri Aug 31 09:17:22 2018被调用
test
60

四,装饰器中的return和通用适配器

>>> def func(func_name):
...     def inner_fun(*args,**kwargs):
...             print("%s在%s被调用"%(func_name.__name__,ctime()))
...             return func_name(*args,**kwargs) #如果不加return会返回None
...     return inner_fun
... 
>>> @func
... def get_info():
...     return "一生平安"
... 
>>> print(get_info())
get_info在Fri Aug 31 09:28:16 2018被调用
一生平安
#一般情况喜爱为了让装饰器更通用,可以有return

五,装饰器带参数,在原有装饰器的基础上

>>> def func_args(args):
...     print("func_args--args=",args)
...     def func(func_name):
...             print("func...func_name=",func_name.__name__)
...             def func_in():
...                     print("func_in...args=",args)
...                     func_name()
...             return func_in
...     return func
... 
>>> @func_args("dtwave")
... def test():
...     print("test.....")
... 
func_args--args= dtwave
func...func_name= test
>>> test()
func_in...args= dtwave
test.....
#1.先执行func_args("dtwave")这个函数,这个函数return的是func函数的引用
#2.@func,返回func_in的引用
#3.使用@func对test进行装饰
#@func_args("dtwave")
#可以理解为:test()==func_args("dtwave")(test)()

场景应用-使用带参的装饰器

>>> def func_args(args):
...     def func(func_name):
...             print("func.....")
...             def func_in():
...                     print("func_in....")
...                     if args == "dtwave":
...                             func_name()
...                             func_name()
...                     elif args == "python":
...                             func_name()
...             return func_in
...     return func
... 
>>> @func_args("dtwave")
... def test():
...     print("test....")
... 
func.....
>>> @func_args("python")
... def test1():
...     print("test1...")
... 
func.....
>>> test()
func_in....
>>> test1()
func_in....
test1...

六,类装饰器
装饰器函数其实是一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象,在python中一般callable对象都是函数,但也有例外,只要对某个对象重写了call()方法,那么这个对象就是callable的。
类装饰器demo

class Test(object):
    def __init__(self,func):
        print("--初始化--")
        print("func name is %s"%func.__name__)
        self.__func = func
    def __call__(self):
        print("---装饰器中的功能---")
        self.__func()
@Test
def test():
    print("----test----")
test()
#说明:1.当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象
#并且会把test这个函数名当做参数传递到__init__方法中,即在__init__方法中的func变量指向了test函数体
#2.test函数相当于指向了用Test创建出来的实例对象
#3.当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
#4.为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用,所以才有了self.__func=func这句代码,从而在调用__call__方法中能够调用到test之前的函数体。

七,多层装饰器

#定义一个函数完成包裹数据<b>xxxx</b>
>>> def make_b(func):
...     def func_in():
...             result = "<b>"+func()+"</b>"
...             return result
...     return func_in
... 
#定一个函数完成包裹数据<i>xxxxx</i>
>>> def make_i(func):
...     def func_in():
...             result = "<i>"+func()+"</i>"
...             return result
...     return func_in
... 
>>> @make_b
... def test1():
...     result = "hello dtwave1"
...     return result
... 
>>> @make_i
... def test2():
...     result = "hello dtwave2"
...     return result
... 
>>> @make_b     #哪个在前面哪个就先被执行
... @make_i
... def test3():
...     result = "hello dtwave3"
...     return result
... 
>>> test1()
'<b>hello dtwave1</b>'
>>> test2()
'<i>hello dtwave2</i>'
>>> test3()
'<b><i>hello dtwave3</i></b>'

猜你喜欢

转载自blog.csdn.net/Hash_Map_zong/article/details/82218486