什么是装饰器
装饰器本质上是一个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>'