什么是装饰器?
python装饰器(fuctional decorators)就是用于拓展原来函数功能的一种函数,目的是在不改变原函数名(或类名)的情况下,给函数增加新的功能。
这个函数的特殊之处在于它的返回值也是一个函数,这个函数是内嵌“原“”函数的函数。
比如:
你新买的毛坯房,装修后变好看了(装修、家具就是新的特效)
孙悟空被放进炼丹炉装饰了一下,出来后,学会了火眼金睛,以前的本领都还在
为什么用装饰器?
因为引入装饰器会便于开发,便于代码复用。
比如有一个函数A,需要添加新的功能,如果你修改原函数A的话会对其它已经引用该函数的地方产生影响,为了避免这样情况,使用装饰器来添加新的功能。
概括的讲,装饰器的作用就是为已经存在的函数或对象添加额外的功能。
装饰器实际上就是对函数进行了包装,它能够在不改变函数的前提下,在这个函数被执行之前或者执行之后执行一段代码.
如何使用装饰器?
工作原理:
假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示:
funA 作为装饰器函数
def funA(fn):
#...
fn() # 执行传入的fn参数
#...
return '...'
@funA
def funB():
#...
实际上,上面程序完全等价于下面的程序:
def funA(fn):
#...
fn() # 执行传入的fn参数
#...
return '...'
def funB():
#...
funB = funA(funB)
通过比对以上 2 段程序不难发现,使用函数装饰器 A() 去装饰另一个函数 B(),其底层执行了如下 2 步操作:
将 B 作为参数传给 A() 函数;
将 A() 函数执行完成的返回值反馈回 B。
显然,被“@函数”修饰的函数不再是原来的函数,而是被替换成一个新的东西(取决于装饰器的返回值),即如果装饰器函数的返回值为普通变量,那么被修饰的函数名就变成了变量名;同样,如果装饰器返回的是一个函数的名称,那么被修饰的函数名依然表示一个函数。
实际上,所谓函数装饰器,就是通过装饰器函数,在不修改原函数的前提下,来对函数的功能进行合理的扩充。
带参数的函数装饰器
如果被修饰的函数本身带有参数,那应该如何传值呢?
最简单的解决方式是用 *args 和 *kwargs 作为装饰器内部嵌套函数的参数,args 和 **kwargs 表示接受任意数量和类型的参数。举个例子:
def funA(fn):
# 定义一个嵌套函数
def say(*args,**kwargs):
fn(*args,**kwargs)
return say
@funA
def funB(arc):
print("C语言中文网:",arc)
@funA
def other_funB(name,arc):
print(name,arc)
funB("http://c.biancheng.net")
other_funB("Python教程:","http://c.biancheng.net/python")
运行结果为:
C语言中文网: http://c.biancheng.net
Python教程: http://c.biancheng.net/python
函数装饰器可以嵌套
上面示例中,都是使用一个装饰器的情况,但实际上,Python 也支持多个装饰器,比如:
@funA
@funB
@funC
def fun():
#...
上面程序的执行顺序是里到外,所以它等效于下面这行代码:
fun = funA( funB ( funC (fun) ) )
实例:
def timer(func):
"""计算一个函数执行的时间"""
print("func is %s" ,func)
def inner(*args,**kwargs):
start = time.time()
ret = func(*args,**kwargs)
print(time.time()-start)
return ret
print("inner is %s" ,inner)
return inner
@timer # ---> func1 = timer(func1)
def func1(a):
time.sleep(2)
print(a)
@timer
def func2(a,b):
time.sleep(2)
print(a,b)
@timer
def func3():
time.sleep(2)
print("Hello World!")
func1(1)
# func2(1,2)
#func3()
执行顺序:
1.先执行timer,申请一块内存地址,相当于门牌号
2.在执行inner,申请一块内存地址
3.把inner函数名传递给func1,func1指向inner函数的内存地址
4.此时执行funct1,也就是执行inner函数
总结:
1.函数即变量
2.高阶函数
a.把一个函数名当作实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能)
b.返回值中包含函数名(不修改函数的调用方式)