什么是装饰器
装饰器(Decorators) 可能是Python中最难掌握的概念之一了,在解释是什么是装饰器之前,我们需要知道,python是一门面向对象的语言,python基本思想就是一切皆对象,数据类型是对象,类是对象,类实例也是对象,所以对于装饰器而言,重要的是函数也是对象!既然对象,就可以进行赋值操作
def func():
print('执行功能ing')
if __name__ == '__main__':
func1 = func
func1()
原来我们定义的函数名是func,但是我们把函数赋值给func1后,也可以通过func1()来调用func函数。
我们也可以把函数当作参数传递给其他函数:
def do_something():
print('正在完成功能')
def func(f):
f()
if __name__ == '__main__':
func(do_somethong)
因为python中函数既可以赋值给其他变量,也可以当作参数传递给其他函数,但是函数也不同于其他变量,函数也有自己的特性,函数内部可以定义函数,可以有返回值:
def func():
def inner_func():
print('我是内部函数')
return inner_fun
if __name__ == '__main__':
func1 = func()
func1()
综上所述,发现有几个特性:
1.可以赋值给其他变量;
2.可以作为参数传递给其他函数;
3.可以在内部定义一个函数;
4.可以当作返回值;
从本质上说,装饰器就是一个函数,它也具有我们上面说的几个特性,并且充分利用了这几个特性。装饰器接受一个普通函数作为参数,并在内部定义了一个函数,在这个内部函数中实现了一些功能,并调用传递进来的函数,最后将内部函数作为返回值返回。如果一个函数把这个步骤全走了一遍,我们就可以认为,这个函数是一个装饰器函数,或者说是装饰器
def func(f):
def inner_func():
print('{}函数开始运行'.format(f.__name__))
f()
print('{}函数运行结束'.format(f.__name__))
return inner_func
def do_something():
print('正在完成的功能')
if __name__ == '__main__':
do_something = func(do_something)
do_something()
在上面代码中,我们将do_something方法作为参数传递给func函数,在func内部我们定义了一个inner_func方法,并在这个方法中添加了函数开始执行和结束执行的提示,在func方法最后,我们将inner__func作为参数返回,然后我们将func函数的返回值赋值给了do_something,所以,最后一次调用的do_something函数已经不再是最初的do_something函数,而是func函数内部定义的inner__func函数,所以最后执行do_something函数时,会有函数开始执行和结束执行的提示功能,而后面在调用do_something函数时,因为返回值是inner_func单纯的函数名,所以执行do_something时,需要加括号,do_something()。
一般装饰器大多是与’@‘符号结合起来用,@符号所实行的功能就是do_something = func(do_something)这行代码的功能。
def func(f):
def inner_func():
print('{}函数开始执行'.format(f.__name__))
f()
print('{}函数结束运行'.format(f.__name__))
return inner_func
@func
def do_something():
print('正在执行功能')
if __name__ == '__main__':
do_something()
输出结果:
do_something函数开始执行
正在执行功能
do_something函数结束运行
为什么要用装饰器
1.可以在不对被装饰函数做任何修改的前提下,给被装饰函数附加一些功能。使用@func对do_something函数进行装饰时,我们没有对do_someting函数代码做任何改动,但是被装饰后的do_something函数却多了其他功能。
2.不改变被装饰函数的调用方式。在被装饰前,我们通过do_something()调用这个函数,被装饰后,还是通过do_something调用这个函数。
3.代码更加精简。如果有多个函数需要添加开始运行和结束运行的提示功能,我们只需要在函数前面添加@func就可以了。
import time
def timmer(f):
def inner_func():
start_ = time.time()
f()
end_ = time.time()
print('{}函数运行消耗时间为:{}'.format(f.__name__,end_-start_))
return inner_func
@timmer
def do_something():
print('函数正在执行')
time.sleep(1)
if __name__ == '__main__':
do_something()
深入理解装饰器
被装饰的函数带返回值
针对有返回值的函数,装饰器该怎么写呢?
def func(f):
def inner_func():
print('{}函数开始执行'.format(f.__name__))
ret = f()
print('{}函数结束运行'.format(f.__name__))
return ret #这里返回值
return inner_func
@func
def do_something():
print('正在完成功能')
return'我是返回值'
if __name__ == '__main__':
ret = do_something()
print(ret)
我们知道,被装饰后的do_something函数其实不再时最初的do_something函数,而是装饰器内部定义的inner_func函数,所以,被装饰的函数的返回值只需要通过装饰器内部定义的inner_func函数返回值返回即可。
被装饰函数带参数
对于装饰器,我们要深刻理解一件事:被装饰后的do_something函数已经不再是原来的do_something函数,而是装饰器内部的inner_func函数。 如果被装饰的函数有参数a,我们还是通过do_something(a)的方式传递输出,不过我们调用do_someting实质调用的inner_func函数,那么在定义装饰器时,定义的inner_func函数时也需要接受参数。
def func(f):
def inner_func(a):
print('{}函数开始运行'.format(f.__name__))
ret = f(a)
print('{}函数结束运行'.format(f.__name__))
return ret
return inner_func
@func
def do_something(a):
print('你好',a)
return '我是返回值'
if __name__ == '__main__':
ret = do_something('姚明')
print(ret)
如果不知道有多少个位置参数,多少个关键字参数时,我们可以用arg和*kwargs
"""
在不改变原有功能(fun01 fun02)调用与定义情况下,
为其增加新功能(打印函数执行时间).
"""
import time
def print_execute_time(func):
def wrapper(*args, **kwargs):
# 记录调用前的时间
start_time = time.time()
result = func(*args, **kwargs)
# 记录调用后的时间
execute_time = time.time() - start_time
print("执行时间是:", execute_time)
return result
return wrapper
@print_execute_time
def fun01():
time.sleep(2) # 睡眠2秒,用于模拟程序执行的过程
print("fun01执行完毕喽")
@print_execute_time
def fun02(a):
time.sleep(1)
print("fun02执行完毕喽,参数是:", a)
if __name__ == '__main__':
fun01()
fun02(100)
装饰器本身带参数
我们上面所写的装饰器都没有参数,或者说只有一个自带的参数,也就是被装饰函数函数名。其实,装饰器也是可以有其他参数的,这样装饰器更加灵活。
def language(lang='中文'):#这里带参数
def func(f):#嵌套一层
def inner_func(*args,**kwargs):
if lang=='中文':
print('{}函数开始运行'.format(f.__name__))
else:
print('The function of {} starts running'.format(f.__name__))
ret = f(*args,**kwargs)
if lang=='中文':
print('{}函数结束运行'.format(f.__name__))
else:
print('The function of {} ends running'.format(f.__name__))
return ret
return inner_func
return func
@language('中文')
def do_something(a):
print('你好',a)
@language('English')
def do_something_2(a):
print('你好,{}!'.format(a))
if __name__ == '__main__':
do_something('姚明')
print('-------------------------')
do_something_2('姚明')
装饰器也是可以多层嵌套使用的,也就是说,一个函数可以被多个装饰器所装饰,执行顺序是从下到上的优先顺序加载装饰
import time
print(1)
def func(f):
print(2)
def inner_func(*name1, **name2):
print('{}函数开始运行……'.format(f.__name__))
f(*name1, **name2)
print('{}函数结束运行……'.format(f.__name__))
print(3)
return inner_func
print(4)
def timmer(f):
print(5)
def inner_timmer(*args, **kwargs):
print('开始计时……')
start_time = time.time()
f(*args, **kwargs)
end_time = time.time()
print('开始结束……')
time_cost = end_time - start_time
print('{}函数运行时长为:{}秒'.format(f.__name__, time_cost))
print(6)
return inner_timmer
print(7)
@func
@timmer
def do_something(name):
time.sleep(1)
print('你好,{}!'.format(name))
print(8)
def do_something_2(name):
time.sleep(1)
print('你好,{}!'.format(name))
if __name__ == '__main__':
print(9)
do_something(name='姚明')
print('-------------------------')
func(timmer(do_something_2))(name='姚明') # 执行效果与上面使用了@符号的do_something一样