装饰器的作用
当我们需要对已开发上线的程序添加某些功能,不能对程序中函数的源代码进行修改,不能改变程序中函数的调用方式就可以使用装饰器。
当然在实现一些通用功能如计算函数调用时间,打日志等,就算能改源码,也最好不要直接改函数,最佳实践还是要用装饰器来实现,对Java熟悉的同学可能已经看出来了,装饰器类似于Java中的AOP(面向切面编程).
高阶函数与闭包
要理解装饰器首先要理解高阶函数胡和闭包的概念,熟悉JavaScript的同学应该对这些概念很熟悉。
在Python中,函数是一等公民,既可以作为参数,又可以作为返回值,当一个函数接收函数作为参数或者返回一个函数,只要满足其中一个条件就可以称为高阶函数
下面代码中pow_2就是一个高阶函数,该函数没啥额外功能,仅演示下
def square(x):
return x**2
def pow_2(fun):
return fun
f = pow_2(square)
f(8)
闭包:延伸了作用域的函数
如果一个函数定义在另一个函数的作用域内,并且引用了外层函数的变量,则该函数称为闭包
闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)
如下代码所示,inner函数就是一个闭包,它可以使用outer函数的环境,也就是他的变量
def outer():
x = 1
z = 10
def inner():
y = x+100
return y, z
return inner
f = outer() # 实际上f包含了inner函数本身+outer函数的环境
print(f)
装饰器的实现
现在实现一个简单的装饰器,计算方法的执行时间
import time
def timer(func):
def inner():
print('我是装饰器,开始计时')
start = time.time()
func()
end = time.time()
print('方法: {}, 共执行了 {:.2f}秒'.format(func.__name__, end-start))
return inner
def f1():
print('我是f1,我在执行任务。。。')
time.sleep(1)
f1 = timer(f1)
f1()
f1是目标方法,timer是修饰方法,里面定义了inner并返回该方法。inner里面定义了输出执行时间的逻辑,并执行了f1方法
f1 = timer(f1)表示f1经过了timer的修饰,后面真正执行的是inner方法
装饰器语法糖
在被修饰的方法前写@修饰器方法名
import time
def timer(func):
def inner():
print('我是装饰器,开始计时')
start = time.time()
func()
end = time.time()
print('方法: {}, 共执行了 {:.2f}秒'.format(func.__name__, end-start))
return inner
@timer
def f1():
print('我是f1,我在执行任务。。。')
time.sleep(1)
f1()
考虑参数和返回值的情况,完全体装饰器代码如下
import time
def timer(func):
def inner(*args, **kwargs):
print('我是装饰器,开始计时')
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print('方法: {}, 共执行了 {:.2f}秒'.format(func.__name__, end-start))
return res
return inner
@timer
def f1(sleep):
print('我是f1,我在执行任务。。。')
time.sleep(sleep)
return sleep
print(f1(2))