装饰器是程序开发中经常会用到的一个功能,程序开发的基础知识,用好了装饰器,开发效率如虎添翼,所以这也是Python面试中必问的问题,但对于好多初次接触这个知识的人来讲,这个功能有点绕,这个都不会,别跟人家说你会Python, 看了下面的文章,保证你学会装饰器。
一、什么是装饰器?
装饰器(Decorator):首先我们需要知道装饰器本身就是一个函数,而这个函数存在的意义就是为其他函数添加附加功能的。
而从某种程度上来说:装饰器并非一个功能特性,它只是一个语法糖,因为从本质上来讲:它实现的是将一个函数或者对象作为参数传递给另外一个函数或者对象,最后再返回新的函数对象,而这个新的函数或者对象内部既实现了原函数的功能,也为其附加了新的功能。
在实现目的-->为其他函数添加附加功能(比如在验证功能基础上添加一个人脸识别),在这个前提下还需要遵循两个原则,即:
①、不修改被修饰函数的源代码。
② 、不修改被修饰函数的调用方式。
- 其语法是: 装饰器以 “@”开头,在定义的函数的上一行调用即加上“@xxx”,xxx为装饰器的函数名,来为这个函数来装饰附加功能
二.装饰器(decorator)功能
- 引入日志
- 函数执行时间统计
- 执行函数前预备处理
- 执行函数后清理功能
- 权限校验等场景
- 缓存
先举个例子:在两开花前添加一个验证功能
def test0():
print("两开花")
test()
写代码要遵循开放封闭
原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
- 封闭:已实现的功能代码块
- 开放:对扩展开发
下面是最原始的改法:
def test0():
print("两开花")
def deco(func):
def warpper():
print("验证")
func()
return warpper
test=deco(test0)
test()
那么使用装饰器会是怎么样呢?
def deco(func):
def warpper():
print("验证")
func()
return warpper
#被修饰函数
@deco #@deco <==> test =deco(test)
def test():
print("两开花")
test()
以上面例子来讲解装饰器的实现原理
python解释器从上往下解释代码。步骤如下:
1.将deco加载到内存里
2.@deco
没错, 从表面上看解释器仅仅会解释这两句代码,因为函数在 没有被调用之前其内部代码不会被执行。
从表面上看解释器着实会执行这两句,但是 @deco 这一句代码里却有大文章, @函数名 是python的一种语法糖。
@deco <<====>>test = deco(test)
@deco内部执行的操作有:
1.执行deco函数,并将@下面的函数作为deco函数的参数,等价于deco(test)
2.将deco(test)的返回值赋值给@下面的函数名test,即test = deco(test)
如此一来,即保证了原功能函数不变,又能给原功能添加新的功能,而且调用方式保持不变,是不是很有用。
接下来给大家说说装饰器的的几种示例
1.无参数的函数
def deco1(func):
def warpper():
func()
print(" 修饰函数")
return warpper
#被修饰函数
@deco1 #@deco <==> test =deco1(test)
def test():
print("test--被修饰函数")
test()
上面代码理解装饰器执行行为可理解成
test = deco1(test)
test先作为deco1函数参数赋值给func,test指向deco1的返回值warpper
test()
调用test(),等价于执行wrapper()
#内部函数warpper引用,所以外部func还未释放,func保存着原始的test
2.被装饰的函数有参数
def deco2(func):
def warpper(name):
func(name)
print(" 修饰函数")
return warpper
#被修饰函数
@deco2 #@deco <==> test =deco1(test)
def test2(name):
print("test--被修饰函数"+name)
test2("lian")
3.被装饰的函数有不定长参数
def deco2(func):
print("hh")
def warpper(*args,**kwargs):
func(*args, **kwargs)
print(" 修饰函数")
return warpper
#被修饰函数
@deco2
def test2(*args, **kwargs):
print("test--被修饰函数",args)
test2(1,2,3,4)
test2(2,3)
4.装饰器中的return
def deco2(func):
def warpper(name):
ret=func(name)
print(" 修饰函数")
return ret
return warpper
#被修饰函数
@deco2
def test2(name):
print("test--被修饰函数"+name)
return "两开花"
test2("lian")
5.装饰器带参数,在原有装饰器的基础上,设置外部变量
def Deco3(max):
def deco3(func):
def warpper():
for i in range(max):
func()
print(" 修饰函数")
return warpper
return deco3
# 下面的装饰过程
# 1. 调用Deco3(max)
# 2. 将步骤1得到的返回值,即deco3返回, 然后deco3(test)
# 3. 将deco3(test)的结果返回,即wrapper
# 4. 让test = wrapper,即test现在指向wrapper
#综述:test() = (Deco3(2))(test)()
#被修饰函数
@Deco3(2)
def test3():
print("test--被修饰函数")
test3()
6.多个装饰器对同一个函数进行装饰
def a1(func):
def b1():
print("执行a1-b1")
func()
return b1
def a2(func):
def b2():
print('执行a2-b2')
func()
return b2
@a1
@a2
def test3():
print("test--被修饰函数")
test3() #====>a1((a2(test3)))()
7.类装饰器
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了 __call__()
方法,那么这个对象就是callable的。
class Foo(object):
def __init__(self,func):
self.func=func
def __call__(self):
print("执行call")
self.func()
@Foo
def foo():
print('foo')
foo()
今天就讲到这里了!