Python装饰器
在说python装饰器前,我们先来讲个故事。
某天,公司主管过来找小明.
主管说:“小明啊,最近公司要新增业务,需要在原来的业务上新增加一些功能,不过好多地方用到原来的地方。你好好想下,明天给我解决方啊。”
小明不假思索地说:“好的”。
第二天,主管来到了小明的面前。主管说:"小明啊,昨天交代你的事情怎么样了?"
小明说:“没问题的。我打算直接修改那个业务的代码,然后再把引用到这部分的代码给修改下”
话还没说完,主管的脸就变红了。大吼:“你知道你这样的任务量有多大吗?公司的代码量怎么多,新业务又要急于上线,你这样要改多久和多麻烦,给你一个晚上再去思考下”å
入世未深的小明经过了一个晚上的深思熟虑。
第三天,来到了主管面前。
小明说:“主管,我想到了一个好办法,可以使用装饰器。不用修改原来的代码,可以在原来的基础上增加一些新的功能。”
主管满意的点了点头。
所以说,装饰器到底是什么东西呢?简而言之,装饰器就是修改其他函数功能的函数,他可以帮助我们的代码更加的简洁。重点:不需要修改原来函数里面的内容。
直接说装饰器,可能会有点难理解和接受。在说装饰器之前,我们先来学习和理解下,什么是闭包和高阶函数。
高阶函数
一个函数可以作为参数传递给另外一个函数,或者一个函数的返回值为另一个函数。满足其中的一个条件就是高阶函数。
参数为函数的函数
def fun1():
print('in the fun1......')
def fun2(fun):
fun()
print('in the fun2')
fun2(fun1)
# in the fun1
# in the fun2
函数的返回值为另一个函数
def fun1(num):
print('in the fun1')
def fun2():
for i in args:
print('in the fun2')
return fun2()
f = fun1(3)
# in the fun1
f()
# in the fun2
# in the fun2
# in the fun2
闭包
我们先来看下百度百科是怎么说的
闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁.
看完之后,发现一堆废话,压根看不懂。
简单来说,一个函数在定义的时候,引用了函数外定义的变量,并且该函数可以在其定义环境外被执行,这种函数我们就叫做闭包。
先来写一个简单的闭包吧。
def counter():
l = [0] # 函数定义外的变量,相对于fun方法而言
def fun():
l[0] += 1 # 引用函数外定义的变量
return l[0]
return fun
c = counter()
print(c()) #1
print(c()) #2
print(c()) #3
# c() 是fun()在其定义环境外被执行
PS
我们先来看下下面的代码
def foo():
l = []
for i in range(1,4):
def bar(i):
return i * i
l.append(bar)
return l
a, b, c = foo()
print(a(),b(),c()) #9,9,9
结果是不是和你想的不一样,这就是典型的闭包陷阱。
原因在于l添加函数的时候,仅仅只是添加,函数没有立即执行,等到后面i的值变为3后再执行,如果不理解再仔细看看几遍吧。
PS: 返回闭包的时候,千万不要使用循环变量或者,后续会发生改变的变量
装饰器
接下来,终于来到我们本章的重点环节了。
装饰器的本质就是一个返回函数的高阶函数。
在python中,一切都是对象。函数也是一个对象,且函数对象也是可以作为变量的,上面我们已经讨论过了。
# 我们先来定义一个累加的函数
def sum(num):
s = 0
for i in range(1, sum+1):
s += i
return s
现在我们想要增强下sum函数的功能,计算下它累加所用的时间,这时候,就是我们装饰器上场的时候了
import time
def calculate_time(func):
a = time.time()
def wrapper(*args, **kwargs):
a = time.time()
res = func(*args, **kwargs)
b = time.time()
print("%s 执行的时间 %s " %(func.__name__, b-a))
return res
return wrapper
@calculate_time
def sum(num):
s = 0
for i in range(1, num+1):
s += i
return s
print(sum(10000))
# sum执行的时间 0.0005550384521484375
# 50005000
是不是感觉很神奇,其实这只是把我们上面学习的做了个变形而已。
把@calcuate_time放在函数的定义处,相当于执行语句
sum = calculate_time(sum)
装饰器是一个高阶函数,其返回结果是个函数,现在sum
指向装饰器返回的函数,sum()
执行的是返回的函数
带参数的装饰器
现在我们的要求又改变了,不应要打印出执行的时间,还要把累加的结果加上一个给定的整数。
需求分析: 打印处执行时间。 这个我们在上面已经解决
结果再加上一个给定的整数。 这个我们可以使用带参数的装饰器
import time
def calculate_time(num):
def decorator(func):
a = time.time()
def wrapper(*args, **kwargs):
a = time.time()
res = func(*args, **kwargs)+num
b = time.time()
print("%s执行的时间 %s " %( func.__name__, b-a))
return res
return wrapper
return decorator
@calculate_time(555)
def sum(num):
s = 0
for i in range(1, num+1):
s += i
return s
print(sum(10000))
# sum执行的时间 0.0005428791046142578
# 50005555
print(sum.__name__)
# wrapper
如果看不懂的话,别着急,看下下面的讲解
@calculate_time(555)
加在定义函数的前面相当于sum = calculate (555)(sum)
calculate_time(555)
执行函数calculatetime(555)
返回decorator
,接下来来到了第二层的嵌套函数,decorator(sum)
返回wrapper
,这时候,sum
指向wrapper
然而现在问题来了
sum
对象变成了wrapper
对象,sum
的属性也都改变了,例如上面提到的sum.__name__
;很容易想到,我们可以在编写wrapper.__name__=sum.__name__
,但是,不需要,python的内置函数functools.wraps
已经帮我们搞定,具体实现看下面代码
不带参数的
import time
import funtools
def calculate_time(func):
a = time.time()
@funtools.wrapper(func)
def wrapper(*args, **kwargs):
a = time.time()
res = func(*args, **kwargs)
b = time.time()
print("%s 执行的时间 %s " %(func.__name__, b-a))
return res
return wrapper
@calculate_time
def sum(num):
s = 0
for i in range(1, num+1):
s += i
return s
print(sum.__name__) # sum
带参数的
import time
def calculate_time(num):
def decorator(func):
a = time.time()
@functools.wrapper(func)
def wrapper(*args, **kwargs):
a = time.time()
res = func(*args, **kwargs)+num
b = time.time()
print("%s执行的时间 %s " %( func.__name__, b-a))
return res
return wrapper
return decorator
@calculate_time(555)
def sum(num):
s = 0
for i in range(1, num+1):
s += i
return s
print(sum(10000))
# sum执行的时间 0.0005428791046142578
# 50005555
print(sum.__name__) # sum
完结!!!!!!!!!