import time
def cal():
res = 0
for i in range(100):
res+=i
time.sleep(1)
print("函数的运行结果为",res)
return res
cal()
import time
def cal():
start_time = time.time()
res = 0
for i in range(100):
res += i
time.sleep(1)
stop_time = time.time()
print("函数的运行结果为", res)
print("函数的运行时间为",stop_time-start_time)
return res
cal()
import time
def cal():
res = 0
for i in range(100):
res+=i
time.sleep(1)
print("函数的运行结果为",res)
return res
def test(func):
print(func)
func()
test(cal)
那么这里的test就是一个高阶函数。因为它接收的参数是一个函数名func,看到这里,我们就可以进行对函数cal添加新的功能了,继续以记录函数运行时间作为一个例子。
import time
def cal():
res = 0
for i in range(100):
res+=i
time.sleep(1)
print("函数的运行结果为",res)
return res
def test(func):
print(func)
start_time = time.time()
func()
stop_time = time.time()
print("函数运行的时间为",stop_time-start_time )
test(cal)
来看下这个函数运行的结果:
那这时候,我们不但没有修改原函数的源代码,而且还为函数添加了一个新的功能,我们的功能就实现了,装饰器就这么结束了。难道真的结束了吗?你注意到了没,函数的方式已经发生改变了,初始我们调用函数使用cal(),而现在却是test(cal),这就违反了我们上述所说的装饰器的第二原则。
那么我们来看下高阶函数第二种定义:返回值是一个函数名
import time
def cal():
res = 0
for i in range(100):
res+=i
time.sleep(1)
print("函数的运行结果为",res)
return res
def test(func):
start_time = time.time()
cal()
stop_time = time.time()
print("函数的运行时间为",stop_time-start_time)
return func
cal = test(cal)
# print(res)
cal()
通过传入参数,能够让我们避免对源代码的修改,但修改了函数的调用方式
通过返回值为函数名,能够让我们避免修改函数的调用方式
这时我们通过对添加功能函数的参数传入为函数,返回值为函数名,此时,我们满足了装饰器的两个原则。我们这种貌似是可取的,其实不然。我们可以发现在添加功能时,需要对函数执行一次才可以,这不符合我们的要求。
因此,仅仅只有高阶函数并不能满足于对我们的需求,不知道还是否记得开头写的对装饰器的知识储备。装饰器=高阶函数+函数嵌套+闭包。接下来,我们来了解什么时闭包吧。
(1)外层函数out_func可以调用内层函数in_func,但无法引用in_func内部的变量y
(2)内层函数in_func可以引用外层函数out_func的变量x
def out_func():
x = 1
def in_func(y):
return x+y
return in_func
test = out_func()
print(test(10))
import time
def timer(func):
def wrapper():
# print(func)
start_time = time.time()
func()
stop_time = time.time()
print(stop_time-start_time)
return wrapper
#@timer #等价于 cal = timer(cal)
def cal():
res = 0
for i in range(100):
res+=i
time.sleep(1)
print("函数的运行结果为",res)
return res
cal = timer(cal)
cal()
事到如此,我们基本 完成了装饰器的功能了,我们不但没有修改函数的源代码,也没有修改其调用方式,还为它添加了一个新的功能。
不过,这样子还存在这一丢丢瑕疵,因为每次给函数添加功能是都需要做一次赋值操作,那能不能不每次调用都赋值一次? 那就要用到了一个 @,这是python提供的一个功能。
@timer 相当于 cal = timer(cal),我们要修饰那个函数,就在那个函数前添加@timer
res = cal()
print("这是函数的返回结果的cal:",res)
def wrapper():
start_time = time.time()
func()
stop_time = time.time()
print(stop_time-start_time)
import time
def timer(func):
def wrapper():
# print(func)
start_time = time.time()
res = func()
stop_time = time.time()
print(stop_time-start_time)
return res
return wrapper
@timer
def cal():
res = 0
for i in range(100):
res+=i
time.sleep(1)
print("函数的运行结果为",res)
return res
res = cal()
print("这是函数的返回结果的cal:",res)
上面的例子,我们的cal函数都是不带参数的,但是,我们并不能保证以后我们所写的每一个函数都是不带参数的。
def timer(func):
def wrapper():
# print(func)
start_time = time.time()
res = func()
stop_time = time.time()
return res
return wrapper
@timer
def cal():
res = 0
for i in range(100):
res+=i
time.sleep(1)
print("函数的运行结果为",res)
return res
@timer
def test1(name):
time.sleep(1)
print("这是%s"%(name))
return res
我们在不修改装饰器的情况下,就会报错了,wrapped函数不需要函数,但是一个给出了。
那么我们就要在wrapped函数中加上参数name,不过,依然不能解决问题。
这是我们就需要用到了可变参数了。 如果我们不确定要往函数中传入多少个参数,或者我们想往函数中以列表和元组的形式传参数时,那就使要用*args; 如果我们不知道要往函数中传入多少个关键词参数,或者想传入字典的值作为关键词参数时,那就要使用**kwargs。
def timer(func):
def wrapper(*args,**kwargs):
# print(func)
start_time = time.time()
res = func(*args,**kwargs)
stop_time = time.time()
return res
return wrapper
这里我们对wrapper函数进行修改,在wrapper中就如位置可变参数和关键字可变参数,这样的话,我们就不用再担心被修饰函数有多少个参数,以及我们定义的装饰器可以修饰任何函数啦。