Python 装饰器
直接进入主题
原代码
以下是原代码,要求给改代码添加统计时间功能
import time def say_hello(): time.sleep(1) print("Hi!") say_hello()
版本1(直接在原函数上修改)
可能有的同学就做出了下面这个版本
import time def say_hello(): start_time=time.time() time.sleep(1) print("Hi!") stop_time=time.time() print('该函数运行时间为:%s' %(stop_time-start_time)) say_hello() #输出: # Hi! # 该函数运行时间为:1.0008747577667236
该版本存在的问题
- 直接修改了函数的原始内容
- 当如果有第二个函数需要实现相同的功能是,需要重新做一遍重复的工作
版本2(将函数当做参数传入)
经过修改上面的版本我们做出了新的代码
import time def run_time(func): start_time=time.time() func() stop_time=time.time() print('该函数运行时间为:%s' %(stop_time-start_time)) def say_hello(): time.sleep(1) print("Hi!") run_time(say_hello)
该版本通过定义一个新的函数 run_time ,然后再新函数中调用原来的函数,实现计算函数的运行时间功能
但是该版本有个问题就是修改了原函数的调用方式,这样很不好。
版本3(修改原函数的内容)
import time def run_time(func): def wrapper(): start_time=time.time() func() stop_time=time.time() print('该函数运行时间为:%s' %(stop_time-start_time)) return wrapper def say_hello(): time.sleep(1) print("Hi!") say_hello=run_time(say_hello) say_hello()
于是乎我们就有了以上的新代码,该代码即没有修改原函数,也没有修改函数的调用方式。
巧妙的通过返回函数 wrapper ,在 执行say_hello=run_time(say_hello) 这句代码将 say_hello 的内容修改。
这基本上算是Python中装饰器的基本架子了。
再把 say_hello=run_time(say_hello) 这句代码优化下就变为下面的版本了。
版本4(关键词@)
import time def run_time(func): def wrapper(): start_time=time.time() func() stop_time=time.time() print('该函数运行时间为:%s' %(stop_time-start_time)) return wrapper @run_time # 这句代码等同于 say_hello=run_time(say_hello) def say_hello(): time.sleep(1) print("Hi!") say_hello()
这个版本的代码算是一个装饰器了,但是我们又有了一个问题,上面的原函数是没有参数,没有返回值的,如果有参数有返回值又该怎么解决呢?请看下一个版本代码
版本5(原函数带有参数、返回值时的实现)
这个版本通过修改 wrapper 函数的参数,并将参数传入 run_time 中传入的 func 函数中,实现了带有参数的调用。
并给wrapper 函数添加 func 函数执行后的返回值。完美实现了需求。
import time def run_time(func): def wrapper(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() print('该函数运行时间为:%s' % (stop_time - start_time)) return res return wrapper @run_time # 这句代码等同于 say_hello=run_time(say_hello) def say_hello(name): time.sleep(1) print(name, ":Hi!") return "我是返回值" print(say_hello("Oliver")) # Oliver :Hi! # 该函数运行时间为:1.000196933746338 # 我是返回值
现在问题又来了,如果某些函数需要直接输出运行时间,而某些函数需要将运行时间写入文件又该怎么处理呢?
版本6(装饰器函数需要参数时实现)
当然有的同学可能会说就写两个装饰器函数呗,但是因为装饰器函数实现的功能基本一致,而如果能想办法给装饰器函数添加一个参数就完美解决这个问题了。请看以下代码
import time def run_time(type): def deco(func): def wrapper(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() content = '该函数运行时间为:%s' % (stop_time - start_time) if type == "print": print(content) elif type == "write_file": with open("运行时间记录", "a+", encoding='utf8') as f: f.write( '{0} {1}:{2}\r\n'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), func.__name__, content)) return res return wrapper return deco @run_time("print") #这段代码相当于 say_hello= run_time("print")(say_hello) ---》也就是 say_hello= deco(say_hello) def say_hello(name): time.sleep(1) print(name, ":Hi!") return "我是返回值" @run_time("write_file") def say_hello2(name): time.sleep(1) print(name, ":你好!") return "我是say_hello2返回值" print(say_hello("Oliver")) print(say_hello2("张三")) # 输出: # Oliver :Hi! # 该函数运行时间为:1.0008816719055176 # 我是返回值 # 张三 :你好! # 我是say_hello2返回值 # 文件内容: # 2018-08-03 10:54:37 say_hello2:该函数运行时间为:1.0004534721374512
版本7(一个函数多个装饰器)
当然一个函数也是可以添加多个装饰器的。比如添加验证功能,并且计算函数运行时间。执行的顺序也是从下往上执行的
如下代码:
import time def verify(func): def wrapper(*args, **kwargs): print("验证一些信息···") res = func(*args, **kwargs) return res return wrapper def run_time(func): def wrapper(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() print('该函数运行时间为:%s' % (stop_time - start_time)) return res return wrapper @verify @run_time def say_hello(name): time.sleep(1) print(name ,":Hi!") say_hello('Oliver') #输出: # 验证一些信息··· # Oliver :Hi! # 该函数运行时间为:1.0001380443572998