Python-装饰器上下五千年和前世今生
Posted 北门吹雪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python-装饰器上下五千年和前世今生相关的知识,希望对你有一定的参考价值。
装饰器上下五千年和前世今生,这里我们始终要问,装饰器为何产生?装饰器产生解决了什么问题?什么样的需求推动了装饰器的产生?思考问题的时候,始终要问,为什么要这样,而不是那样或者其他样。这里我不先说,也不直接把装饰器的最终样子摆出来,而是说说装饰器发展过程,从这些过程中知道,不是技术推动技术的发展,而是解决这个需求推动技术的产生。接下来一步步构建装饰器产生的过程,从最原始的方向来到最新的状态来解说装饰器为何产生,装饰器产生的过程是如何演变的。
下面是一段简代码,实现的功能是暂停1秒,然后再打印一句问好"Hai, 北门吹雪"
import time
def bei_men_chui_xue():
time.sleep(1)
print("Hai, 北门吹雪")
bei_men_chui_xue()
这里需求来了,谁谁谁,总是在提需求,杀了祭天。
哎呀,在这个功能上在添加一个小小的功能,就是一个小小功能,输出一下这个功能的运行时间,不难吧?
默默的掏出身后的板砖,啪的一下拍在桌子上,昨天晚上你不是这样说的,说好不改需求的,今天早上就翻脸了?
好吧,我默默的去改需求了,提交了方案 1
import time
def bei_men_chui_xue():
time.sleep(1)
print("Hai, 北门吹雪")
start_time = time.time()
bei_men_chui_xue()
print("run time:", time.time() - start_time)
虽然实现了这个需求,但是这个直接嵌入代码逻辑,把功能代码逻辑包围了起来,看起来不够优雅,改得好看点好么?
好吧,我又默默器去修改需求了,提交了方案2
import time
def bei_men_chui_xue():
time.sleep(1)
print("Hai, 北门吹雪")
def get_run_time(func):
start_time = time.time()
func()
end_time = time.time()
print(end_time - start_time)
# 高阶函数,已经很优雅的解决方案
get_run_time(bei_men_chui_xue)
很好,使用了高阶函数,居然知道Python中一切皆对象的原理,把获取时间的功能封装成一个函数,试试用闭包去实现?
好吧,我又默默器去修改需求了,提交了方案3
import time
def bei_men_chui_xue():
time.sleep(1)
print("Hai, 北门吹雪")
def get_run_time(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print(end_time - start_time)
return wrapper
# 闭包解决方案,看起来方案2远比这个优雅
f = get_run_time(bei_men_chui_xue)
f()
嗯,非常好,我也觉得方案2好,但是你修改了源码,改变了源码的执行逻辑,尝试不改变源码逻辑?
好吧,我又默默器去修改需求了,提交了方案4
import time
def get_run_time(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print(end_time - start_time)
return wrapper
@get_run_time
def bei_men_chui_xue():
time.sleep(1)
print("Hai, 北门吹雪")
# 装饰器解决方案,前面的语法糖才是装饰器核心
bei_men_chui_xue()
不错不错,装饰器把被装饰函数传递进装饰器,调用源函数其实本质上调用装饰器中的wrapper函数,我想在原函数传递进去参数,如何?
好吧,我又默默器去修改需求了,提交了方案5
import time
def get_run_time(func):
def wrapper(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
print(end_time - start_time)
return wrapper
@get_run_time
def bei_men_chui_xue(name):
time.sleep(1)
print("Hai, %s" % name)
# 解决装饰的函数有变量
bei_men_chui_xue("北门吹雪")
很好,很好,通过往装饰器中传递 *args **kwargs参数完成向原函数传递参数,我想获得一下原函数的返回值?如被装饰函数的返回值?
好吧,我又默默器去修改需求了,提交了方案6
import time
def get_run_time(func):
def wrapper(*args, **kwargs):
start_time = time.time()
r = func(*args, **kwargs)
end_time = time.time()
run_time = end_time - start_time
print(run_time)
# 被装饰函数返回值
return r
return wrapper
@get_run_time
def bei_men_chui_xue(name):
time.sleep(1)
return "Hai, %s" % name
# 解决装饰的函数有变量
r = bei_men_chui_xue("北门吹雪")
print(r)
功能还需要改动,我不想输出其运行时间,可以通过向装饰器中传入参数,如果运行时间超过这个数打印已经超时?
好吧,我又默默器去修改需求了,提交了方案7
import time
def get_run_time(time_out):
def out_wrapper(func):
def wrapper(*args, **kwargs):
start_time = time.time()
r = func(*args, **kwargs)
end_time = time.time()
# 获取运行时间
run_time = end_time - start_time
# 检查程序运行时间是否超时
if run_time > time_out:
print("运行时间已经超时")
# 被装饰函数返回值
return r
return wrapper
return out_wrapper
@get_run_time(time_out=1)
def bei_men_chui_xue(name):
time.sleep(1)
return "Hai, %s" % name
bei_men_chui_xue("北门吹雪")
# 获取返回值
r = bei_men_chui_xue("北门吹雪")
print(r)
完美,这个才是五彩斑斓的黑,加个鸡腿
从这个过程中可以看出,被装饰函数运行时候其实运行的是装饰器内部wrapper函数,通过函数闭包实现对一些参数状态的保存,从而实现各种需求的装饰器
以上是关于Python-装饰器上下五千年和前世今生的主要内容,如果未能解决你的问题,请参考以下文章