装饰器上下五千年和前世今生,这里我们始终要问,装饰器为何产生?装饰器产生解决了什么问题?什么样的需求推动了装饰器的产生?思考问题的时候,始终要问,为什么要这样,而不是那样或者其他样。这里我不先说,也不直接把装饰器的最终样子摆出来,而是说说装饰器发展过程,从这些过程中知道,不是技术推动技术的发展,而是解决这个需求推动技术的产生。接下来一步步构建装饰器产生的过程,从最原始的方向来到最新的状态来解说装饰器为何产生,装饰器产生的过程是如何演变的。

下面是一段简代码,实现的功能是暂停1秒,然后再打印一句问好"Hai, 北门吹雪"

import time

def bei_men_chui_xue():
time.sleep(1)
print("Hai, 北门吹雪") bei_men_chui_xue()

这里需求来了,谁谁谁,总是在提需求,杀了祭天。

哎呀,在这个功能上在添加一个小小的功能,就是一个小小功能,输出一下这个功能的运行时间,不难吧?

默默的掏出身后的板砖,啪的一下拍在桌子上,昨天晚上你不是这样说的,说好不改需求的,今天早上就翻脸了?

好吧,我默默的去改需求了,提交了方案 1

# 这里通过直接在功能函数bei_men_chui_xue前面添加一个获取开始时间的时间戳,然后再函数bei_men_chui_xue后面获取当前时间戳减去时间的时间戳,得到函数bei_men_chui_xue的运行时间,简单粗暴,嵌入了代码逻辑

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

# 这里我把获取函数bei_men_chui_xue运行时间功能封装成函数get_run_time,把函数bei_men_chui_xue对象当作参数传入函数get_run_time中,然后在函数get_run_time运行并统计这个函数的运行时间,这个已经是非常优雅的解决方案,但还是要改变源代码

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

# 这里使用的函数闭包去解决这个需求,闭包最大特性函数中嵌套函数,保留上层函数的变量,其实本质上上和方案2没有区别

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

# 这里通过@语法糖符号,直接在函数上添加这个语法糖,给函数添加上以前未有的功能,不改变源代码执行逻辑顺序,是个比方案2更加优秀的方案,其实本质上和方案3没有区别,也是利用闭包函数的特性,然后在语法上进行规范,抽象为@,就像装饰在函数上,语法糖又被称为装饰器,用函数get_run_time去装饰函数bei_men_chui_xue添加上统计运行时间的功能,执行原函数bei_men_chui_xue本质上被置换为执行wrapper函数,通过闭包特性保留上层函数的变量。

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

# 执行原函数bei_men_chui_xue其实本质上执行 wrapper函数,在wrapper函数中保存原函数func的执行结果,最后返回回去

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

# 原函数有多个参数,我不关心参数到底是什么,只需要wrapper接收 *args **kwargs ,真正函数 func也接收这两个参数

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-装饰器(语法糖)上下五千年和前世今生的更多相关文章

  1. python装饰器 语法糖

    简介: 装饰器(Decorators)是 Python 的一个重要部分.简单地说:他们是修改其他函数的功能的函数. 比如说我们写flask,路由就是用装饰器定义的.如果写权限控制,那么权限控制一般也是 ...

  2. python 装饰器(语法糖)

    def  login(func):    def testlogin():        for  i in range(3):            _username="abc" ...

  3. python装饰器语法

    @就是decorator,早Python的高效开发中会用到,当然和java的annotation有一定的相似,但又不完全相同,看这篇文章:https://blog.csdn.net/zkp_987/a ...

  4. python装饰器总结

    一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.简单的说装饰器就是一个用来返回函数的函数 ...

  5. Python 装饰器入门(上)

    翻译前想说的话: 这是一篇介绍python装饰器的文章,对比之前看到的类似介绍装饰器的文章,个人认为无人可出其右,文章由浅到深,由函数介绍到装饰器的高级应用,每个介绍必有例子说明.文章太长,看完原文后 ...

  6. Python 装饰器的诞生过程

    ​ Python中的装饰器是通过利用了函数特性的闭包实现的,所以在讲装饰器之前,我们需要先了解函数特性,以及闭包是怎么利用了函数特性的 ① 函数特性 Python中的函数特性总的来说有以下四点: 1. ...

  7. Python装饰器AOP 不定长参数 鸭子类型 重载(三)

    1 可变长参数与关键字参数 *args代表任意长度可变参数 **kwargs代表关键字参数 用*args和**kwargs只是为了方便并没有强制使用它们. 缺省参数即是调用该函数时,缺省参数的值若未被 ...

  8. Python装饰器完全解读

    1 引言 装饰器(Decorators)可能是Python中最难掌握的概念之一了,也是最具Pythonic特色的技巧,深入理解并应用装饰器,你会更加感慨——人生苦短,我用Python. 2 初步理解装 ...

  9. Python 装饰器执行顺序

    Python 装饰器执行顺序 之前同事问到两个装饰器在代码中使用顺序不同会不会有什么问题,装饰器是对被装饰的函数做了一层包装,然后执行的时候执行了被包装后的函数,例如: def decorator_a ...

随机推荐

  1. Linux环境编程进程间通信机制理解

    一.Linux系统调用主要函数 二.创建进程 1.创建子进程系统调用fork() 2.验证fork()创建子进程效果 3.系统调用fork()与挂起系统调用wait() 三.模拟进程管道通信 四.pi ...

  2. vue前端工程化

    今日目标 1.能够了解模块化的相关规范 2.了解webpack3.了解使用Vue单文件组件4.能够搭建Vue脚手架 5.掌握Element-UI的使用 1.模块化的分类 A.浏览器端的模块化   1) ...

  3. Python 带你一键生成朋友圈超火的九宫格短视频

    1. 场景 如果你经常刷抖音和微信朋友圈,一定发现了最近九宫格短视频很火! ​从朋友圈九宫格图片,到九宫格视频,相比传统的图片视频,前者似乎更有个性和逼格 除了传统的剪辑软件可以实现,是否有其他更加快 ...

  4. Android开发之去掉listview的点击效果,一行代码间接粗暴,解决你的问题。

    作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985 Android开发之去掉listview的点击效果,一行代码间接粗暴,解决你的问题. 当你在用list ...

  5. private protected internal public

    //C#中的访问修饰符: //private,私有访问修饰符,被private访问修饰符修饰的成员只有在当前类的内部可以访问,其他地方一律不能访问[类中成员,如果不写访问修饰符则默认都是私有的] // ...

  6. 基于模板特化的Lua自动绑定系统

    LuaBind http://www.rasterbar.com/products/luabind.html http://blog.sina.com.cn/s/blog_646817c00100gk ...

  7. 第四方 fast快捷支付封装

    class Fastpay { protected $conf = [ 'appkey'=>'',//appkey 'key'=>'',//秘钥 ]; protected $http_ty ...

  8. 调手表(bfs)

    题目描述 小明买了块高端大气上档次的电子手表,他正准备调时间呢.在 M78 星云,时间的计量单位和地球上不同,M78 星云的一个小时有 n 分钟.大家都知道,手表只有一个按钮可以把当前的数加一.在调分 ...

  9. ZT:通过Find命令找到你要找的东西

    https://os.51cto.com/art/202003/612049.htm find 命令有巨多的选项可以帮助你准确定位你在 Linux 系统上需要寻找的文件.这篇文章讨论了一系列非常有用的 ...

  10. GuestOS? HostOS?

    起因 今天在网上看到一篇文章  有几个陌生的关键词不太熟悉,就随笔记一下. 名词解释 # OS :操作系统 # VM(虚拟机)    里的OS 称为        GuestOS # 物理机      ...