我们之前在入门总结的时候,写了一个模拟停车场收费的程序,主要逻辑就是利用车辆入场时间和出场时间间隔来计费。如果现在我们想要计算一个函数的执行时间,也可以这样计算时间间隔。
from datetime import datetime
import time
def fib2(n):
start = datetime.now()
a,b = 0,1
result = []
while n>1:
result.append(b)
a,b = b,a+b
n -= 1
print(result)
time.sleep(2)
end = datetime.now()
print('函数:{},时间:{}'.format(fib2.__name__,(end-start).seconds))
return result
我们以之前的求斐波那契数列为例,在函数的开始和结束时加入时间记录,最后输出函数名称__name__(python函数内置属性)和执行时间。显然这样统计时间会侵入到函数中,而且这种可以公用的东西最好单独定义一个函数。
这种情况下,我们可以使用装饰器(decorator),在调用函数的时候动态增强代码功能,简单来说就是一个包装函数,由包装函数触发目标函数。下面我们看一下这个包装函数。
def timeelaps(func):
def wrapper(*tuple_args,**dict_args):
start = datetime.now()
result = func(*tuple_args,**dict_args)
end = datetime.now()
print((end-start).seconds)
return result
return wrapper
@timeelaps
def hello():
print('hello','小麦’)
如上,我们使用一个可以传入目标函数的函数,然后在该函数中执行需要增强的代码,然后再调用目标函数,然后我们使用@函数名注解到目标函数上。
此时执行hello()实际是执行hello=timeelpas(hello),此时的hello已经指向了包装函数timeelpas,返回的是wrapper函数,在wrapper中又调用了目标函数。唯一美中不足的就是此时hello的函数名称变成了wrapper,破坏了对象的完整性。我们可以使用@functools.wraps(func)来保持原有对象信息。只需加在wrapper函数上。下面我们看一下包装函数也需要参数的情况。
import functools
def timeelaps1(title):
def decorator(func):
@functools.wraps(func)
def wrapper(*tuple_args,**dict_args):
print(title)
return func(*tuple_args,**dict_args)
return wrapper
return decorator
@timeelaps1('小麦')
def hello():
print('hello','小麦')
同理这里的hello实际等价于hello=timeelaps1('小麦')(hello),返回一个decorator函数,然后传入hello函数在wrapper中执行,简单理解就是把上面的包装函数又包装了一层。
装饰器的思想就就是装饰模式,在不改变原代码的情况下,通过继承组合动态扩展对象的功能,也就是通过装饰来包装真实对象,今天我们就补充到这里。下一节我们继续讲文件IO。