本文始发于个人公众号:TechFlow,原创不易,求个关注

今天是Python专题的第12篇文章,我们来看看Python装饰器。

一段囧事

差不多五年前面试的时候,我就领教过它的重要性。那时候我Python刚刚初学乍练,看完了廖雪峰大神的博客,就去面试了。我应聘的并不是一个Python的开发岗位,但是JD当中写到了需要熟悉Python。我看网上的面经说到Python经常会问装饰器,我当时想的是装饰器我已经看过了,应该问题不大……

没想到面试的时候还真的问到了,面试官问我Python当中的装饰器是什么。由于紧张和遗忘,我支支吾吾了半天也没答上来。我隐约听到了电话那头的一声叹息……

时隔多年,我已经不记得那是一家什么公司了(估计规模也不大),但装饰器很重要这个事情给我深深打下了烙印。

装饰器本质

如今如果再有面试官问我Python中的装饰器是什么,我一句话就能给回答了,倒不是我装逼,实际上也的确只需要一句话。Python中的装饰器,本质上就是一个高阶函数。

你可能不太清楚高阶函数的定义,没关系,我们可以类比一下。在数学当中高阶导数,比如二次导数,表示导数的导数。那么这里高阶函数自然就是函数的函数,结合我们之前介绍过的函数式编程,也就是说是一个返回值是函数的函数。但是这个定义是充分不必要的,也就是说装饰器是高阶函数,但是高阶函数并不都是装饰器。装饰器是高阶函数一种特殊的用法。

任意参数

在介绍装饰器的具体使用之前,我们先来了解和熟悉一下Python当中的任意参数。

Python当中支持任意参数,它写成*args, **kw。表示的含义是接受任何形式的参数。

举个例子,比如我们定义一个函数:

def exp(a, b, c='3', d='f'):

print(a, b, c, d)

我们可以这样调用:

args = [1, 3]

dt = {'c': 4, 'd': 5}

exp(*args, **dt)

最后输出的结果是1, 3, 4, 5。也就是说我们用一个list和dict可以表示任何参数。因为Python当中规定必选参数一定写在可选参数的前面,而必选参数是可以不用加上名称标识的,也就是可以不用写a=1,直接传入1即可。那么这些没有名称标识的必选参数就可以用一个list来表示,而可选参数是必须要加上名称标识的,这些参数可以用dict来表示,这两者相加可以表示任何形式的参数。

注意我们传入list和dict的时候前面加上了*和**,它表示将list和dict当中的所有值展开。如果不加的话,list和dict会被当成是整体传入。

所以如果一个函数写成这样,它表示可以接受任何形式的参数。

def exp(*args, **kw):

pass

定义装饰器

明白了任意参数的写法之后,装饰器就不难了。

既然我们可以用*args, **kw接受任何参数。并且Python当中支持一个函数作为参数传入另外一个函数,如果我们把函数和这个函数的所有参数全部传入另外一个函数,那么不就可以实现代理了吗?

还是刚才的例子,我们额外增加一个函数:

def exp(a, b, c='3', d='f'):

print(a, b, c, d)

def agent(func, *args, **kwargs):

func(*args, **kwargs)

args = [1]

dt = {'b': 1, 'c': 4, 'd': 5}

agent(exp, *args, **dt)

装饰器的本质其实就是这样一个agent函数,但是如果使用的时候需要手动传入会非常麻烦,使用起来不太方便。所以Python当中提供了特定的库,我们可以让装饰器以注解的方式使用,大大简化操作:

from functools import wraps

def wrapexp(func):

def wrapper(*args, **kwargs):

print('this is a wrapper')

func(*args, **kwargs)

return wrapper

@wrapexp

def exp(a, b, c='3', d='f'):

print(a, b, c, d)

args = [1, 3]

dt = {'c': 4, 'd': 5}

exp(*args, **dt)

在这个例子当中,我们定义了一个wrapexp的装饰器。我们在其中的wrapper方法当中实现了装饰器的逻辑,wrapexp当中传入的参数func是一个函数,wrapper当中的参数则是func的参数。所以我们在wrapper当中调用func(*args, **kw),就是调用打上了这个注解的函数本身。比如在这个例子当中,我们没有做任何事情,只是在原样调用之前多输出了一行’this is a wrapper',表示我们的装饰器调用成功了。

装饰器用途

我们理解了装饰器的基本使用方法之后,自然而然地会问一个天然的问题,学会了它究竟有什么用呢?

如果你从上面的例子当中没有领会到装饰器的强大,不如让我用一个例子再来暗示一下。比如说你是一个程序员,辛辛苦苦做出了一个功能,写了好几千行代码,上百个函数,终于通过了审核上线了。这个时候,你的产品经理找到了你说,经过分析我们发现上线的功能运行速度不达标,经常有请求超时,你能不能计算一下每个函数运行的耗时,方便我们找到需要优化的地方?

这是一个非常合理的请求,但想想看你写了上百个函数,如果每一个函数都要手动添加时间计算,这要写多少代码?万一哪个函数不小心改错了,你又得一一检查,并且如果要求严格的话你还得为每一个函数专门写一个单元测试……

我想,正常的程序员应该都会抗拒这个需求。

但是有了装饰器就很简单了,我们可以实现一个计算函数耗时的装饰器,然后我们只需要给每一个函数加上注解就好了。

import time

from functools import wraps

def timethis(func):

def wrapper(*args, **kwargs):

start = time.time()

result = func(*args, **kwargs)

end = time.time()

print(func.__name__, end-start)

return result

return wrapper

这也是装饰器最大的用途,可以在不修改函数内部代码的前提下,为它包装一些额外的功能。

元信息

我们之前说过装饰器的本质是高阶函数,所以我们也可以和高阶函数一样来调用装饰器,比如下面这样:

def exp(a, b, c='3', d='f'):

print(a, b, c, d)

args = [1, 3]

dt = {'c': 4, 'd': 5}

f = wrapexp(exp)

f(*args, **dt)

这样的方式得到的结果和使用注解是一样的,也就是说我们加上注解的本质其实就是调用装饰器返回一个新的函数。

既然和高阶函数是一样的,那么就带来了一个问题,我们使用的其实已经不再是原函数了,而是一个由装饰器返回的新函数,虽然这个函数的功能和原函数一样,但是一些基础的信息其实已经丢失了。

比如我们可以打印出函数的name来做个实验:

正常的函数调用__name__返回的都是函数的名称,但是当我们加上了装饰器的注解之后,就会发生变化,同样,我们输出加上了装饰器注解之后的结果:

我们会发现输出的结果变成了wrapper,这是因为我们实现的装饰器内部的函数叫做wrapper。不仅仅是__name__,函数内部还有很多其他的基本信息,比如记录函数内描述的__doc__,__annotations__等等,这些基本信息被称为是元信息,这些元信息由于我们使用注解发生了丢失。

有没有什么办法可以保留这些函数的元信息呢?

其实很简单,Python当中为我们提供了一个专门的装饰器用来保留函数的元信息,我们只需要在实现装饰器的wrapper函数当中加上一个注解wraps即可。

def wrapexp(func):

@wraps(func)

def wrapper(*args, **kwargs):

print('this is a wrapper')

func(*args, **kwargs)

return wrapper

加上了这个注解之后,我们再来检查函数的元信息,会发现它和我们预期一致了。

总结

了解了Python中的装饰器之后,再来看之前我们用过的@property, @staticmethod等注解,想必都能明白,它们背后的实现其实也是装饰器。灵活使用装饰器可以大大简化我们的代码,让我们的代码更加规范简洁,还能灵活地实现一些特殊的功能。

装饰器的用法很多,今天介绍的只是其中最基本的,在后续的文章当中,还会继续和大家分享它更多其他的用法。在文章开始的时候我也说了,装饰器是Python进阶必学的技能之一。想要熟练掌握这门语言,灵活运用,看懂大佬的源码,装饰器是必须会的东西。

希望大家都能有所收获,原创不易,厚颜求个赞和关注~

python装饰器带参数函数二阶导数公式_一文搞定Python装饰器,看完面试不再慌相关推荐

  1. python装饰器带参数函数二阶导数公式_【计算机程序的构造和解释】使用函数构建抽象——5. 高阶函数...

    学Python,用RPA 艺赛旗RPA2020.1版本 正在免费下载使用中,欢迎下载使用艺赛旗-RPA机器人免费下载|提供流程自动化解决方案​www.i-search.com.cn 我们已经看到,函数 ...

  2. python装饰器带参数函数二阶导数公式_机器学习【二】单变量线性回归

    吴恩达机器学习笔记整理--单变量线性回归 通过模型分析,拟合什么类型的曲线. 一.基本概念 1.训练集 由训练样例(training example)组成的集合就是训练集(training set), ...

  3. python装饰器带参数函数二阶导数公式_SICP Python 描述 1.6 高阶函数

    1.6 高阶函数 我们已经看到,函数实际上是描述复合操作的抽象,这些操作不依赖于它们的参数值.在square中, >>> def square(x): return x * x 我们 ...

  4. python装饰器带参数函数二阶导数公式_MVision/caffe_简介_使用.md at master · Ewenwan/MVision · GitHub...

    2. caffe 模型配置文件 prototxt 详解 每个模型由多个 层 构成 layer {{{{ name: "{}" #层名字,可随意取名 type: "{}&q ...

  5. python语言语句快的标记是什么_一文搞懂Python程序语句

    原标题:一文搞懂Python程序语句 程序流 Python 程序中常用的基本数据类型,包括: 内置的数值数据类型 Tuple 容器类型 String 容器类型 List 容器类型 自然的顺序是从页面或 ...

  6. python中函数不包括参数函数二阶导数公式_请问参数方程确定的函数的二阶导数公式的详细推导过程?...

    展开全部 y''=d(dy/dx)/dx=[d(dy/dx)/dt]*(dt/dx) 因变量由y换作dy/dx,自变量还是62616964757a686964616fe58685e5aeb931333 ...

  7. python中函数不包括参数函数二阶导数公式_参数方程的二阶导数怎么求????...

    展开全部 设参数方程 x(t), y(t),则二阶导数: 一阶32313133353236313431303231363533e4b893e5b19e31333431373836导数是自变量的变化率, ...

  8. python 如何判断一个函数执行完成_三步搞定 Python 中的文件操作

    当程序运行时,变量是保存数据的好方法,但变量.序列以及对象中存储的数据是暂时的,程序结束后就会丢失,如果希望程序结束后数据仍然保持,就需要将数据保存到文件中. Python 提供了内置的文件对象,以及 ...

  9. 一文搞定Python中的时间转化

    一文搞定Python中的时间转化 在生活和工作中,我们每个人每天都在和时间打交道: 早上什么时候起床? 地铁几分钟来一趟? 中午什么时候开始午休? 明天是星期几? 距离上次买衣服已经2个月呢? 领导让 ...

最新文章

  1. Mysql主从同步异常处理
  2. 【自动驾驶】3. DDS 数据分发服务(Data Distribution Service)
  3. Source Insight中查看文件显示全路径
  4. js发送get、post请求的方法简介
  5. web前端三大主流框架_web前端三大主流框架
  6. php如何查询mysql数据库字符集_修改及查看mysql数据库的字符集_MySQL
  7. SVN工作副本已经锁定错误的解决方法
  8. 技术对游戏公司来讲到底有多重要?
  9. jsp获取连接池的实时连接数_数据库连接池原理分析及模拟实现
  10. 现在有些公众号我真的看不惯
  11. u盘装了linux后无法格式化,U盘制作ubuntu安装盘后无法格式化处理
  12. HTML教程: 网页字体的设置浅谈
  13. 存储卡的使用方法大全
  14. UV材质图片合并后UV坐标的变换
  15. 苹果手机投影_手机和投影同时用流量能否投屏
  16. 微信小程序开发教程(一)--注册小程序、下载开发工具及新建工程
  17. 脑部CT检查图像检查床去除算法
  18. C# 中的Tag属性
  19. 马上2023了,一起来了解Python未来的发展趋势!
  20. python复习加总结

热门文章

  1. SAP 导出 HTML,【我sap这导出数据表格export.mhtml怎么转换为 excel 工作表.xlsx】excel生成html表格数据...
  2. 企业微信H5_网页jssdk调用,ticket签名config及示例
  3. VS Code 报错Vetur can‘t find ‘tsconfig.json‘ or ‘jsconfig.json‘的解决方法
  4. Hadoop集群安装部署_分布式集群安装_01
  5. SpringBoot2 集成 xxl-job任务调度中心_阻塞策略
  6. (需求实战_进阶_05)SSM集成RabbitMQ 通配符模式 关键代码讲解、开发、测试
  7. FileZilla 下载安装使用
  8. java虚拟机中xms_java JVM虚拟机选项: Xms Xmx PermSize MaxPermSize 区别
  9. python三菱_三菱机器人melfarxm.ocx控件的Python使用,MelfaRxMOCX,python,用法
  10. cmd输入pip报错_pyhon3下pip安装使用教程(win10)