一文带你探寻Python中的装饰器

作者:真的不能告诉你我的名字 时间:2021-07-11 10:17:59 

试官: 听说你熟悉python,那么你能简单阐述一下python的装饰器、生成器以及迭代器么?

我: emm, 我不清楚,我只是了解过python最基本的代码。

上述是弟弟前段时间去面试运维开发,遇到的问题,emmm,运维是一个很杂的职业,在小公司,总结一句话就是宽而浅,痛定思痛,决定来了解一下python特性,于是乎,就有了这篇文章。

这边文章,我们将介绍python生成器,使用环境为: Python 3.6.8

一文带你探寻Python中的装饰器

什么是装饰器

要理解装饰器之前,我们需要了解什么是闭包函数。

闭包函数

我们简单写个demo,再解释一下什么是闭包函数。

def exterFunc(x):
 def innerFunc(y):
   return x * y

return innerFunc

def main() -> None:
 f = exterFunc(6)
 result = f(5)

print(result)

if __name__ == '__main__':
 main()

可以看到,上述代码所示,所谓的闭包函数是指: 闭包函数是指在函数中再定义函数,内部函数可以访问外部的变量,在外部函数中,将内部函数作为返回值返回。

可以看到上述例子中,我们定义了exterFunc的函数,它将接收一个形参x,在exterFunc函数中又中定义了innerFunc,它也接收一个形参y, 在innerFunc函数中,返回x * y,没错,内部函数可以访问外部函数传入的变量,最后将exterFunc作为返回值返回,这就是闭包函数。

最简单的装饰器

装饰器是一种很特殊的函数,可以接收函数作为形参,且返回一个新的函数,在我们上一篇介绍生成器的时候,还记得我们使用memory_profiler库来打印函数的内存运行情况么? 这就是用的装饰器。

一文带你探寻Python中的装饰器

我们可以写个最简单的例子,来阐述一下python装饰器,即:

def foo(func):
   def wrapper():
       print("装饰器开始运行了")
       func()
       print("装饰器结束运行了")

return wrapper

@foo
def sayHello():
   print("hello pdudo in juejin")

def main() -> None:
 sayHello()

if __name__ == '__main__':
 main()

上面代码,我们定义了一个装饰器foofoo需要传入一个函数, foo内部有一个函数wrapper。这样的函数中包函数,我们将其称之为闭包函数,后面会介绍闭包函数。言归正传,在wrapper函数中,我们可以在运行func函数的时候,再其执行前后语句。

需要调用装饰器的时候,只需要@加上函数名称即可。

为什么需要装饰器

要解释这个问题,我们可以看来了解下,装饰器解决了一些什么问题:

  • 解决代码重复性,对于经常需要实现类似的功能而言,可以将该功能抽离出来,作为装饰器来调用,从而避免代码重复。

  • 增强代码可读性,在不修改原始代码的前提下,可以利用装饰器在函数前后增加代码,例如 处理异常、记录日志等等,可以利用装饰器将附加功能和函数主要功能分开,增加代码可读性。

说了那么多,我们来列举一个最简单的例子,利用装饰器打印一下函数的运行时间。

import time

def getExecTimers(func):
 def wrapper():
   startTimes = time.time()
   func()
   endTimes = time.time()
   print("函数运行时间: " , endTimes - startTimes ,"s")
 return wrapper

@getExecTimers
def testFunc():
 print("开始执行函数")
 time.sleep(5)
 print("函数执行结束")

def main() -> None:
 testFunc()

if __name__ == '__main__':
 main()

这个装饰器,会记录函数的运行时间。可以看到,我们为这个函数增加了一个附属功能,但是又没有修改到原始函数。

上述案例,应该可以证明为什么需要使用装饰器了。

装饰器用法

上述我们讨论了最简单的装饰器写法,并且写了一个小功能,即打印函数的运行时间。接下来,我们要看下装饰器的其他写法。

不是用语法糖调用

还记得上面我们调用装饰器,是使用的@+装饰器名称么? 其实这是python的语法糖,如果不是用语法糖的话,应该是这样来使用的:

def foo(func):
   def wrapper():
       print("装饰器开始运行了")
       func()
       print("装饰器结束运行了")

return wrapper

def sayHello():
   print("hello pdudo in juejin")

def main() -> None:
 f1 = sayHello
 f2 = foo(f1)

f2()

if __name__ == '__main__':
 main()

完整的写法应该如下代码所示,这是一个完整的闭包调用逻辑。

f1 = sayHello
f2 = foo(f1)

f2()

而在函数前加上@+装饰器名称, 是一种python的语法糖

带参数的装饰器

这里要做一个铺垫,在python中,有2个特殊的变量,分别为*args**kwargs,都是用来处理不定量参数的,分别代表的含义为:

  • *args: 将会将参数打包为元组

  • **kwargs: 将会打包字典传递给函数

def foo(func):
   def wrapper(*args,**kwargs):
       print("装饰器开始运行了")
       print("装饰器捕获到的参数: " ,args,**kwargs)
       func(*args,**kwargs)
       print("装饰器结束运行了")

return wrapper

@foo
def sayHello(a,b,c,dicts):
   print("传入的参数: " , a,b,c)
   print("传入的参数: " , dicts)

def main() -> None:
 sayHello(1,2,3,{"name":"juejin"})

if __name__ == '__main__':
 main()

在装饰器中,若我们要给函数传递参数,是需要先将参数传递给装饰器,而在装饰器中接收后再进行传递的,所以代码才会是这样的:

def foo(func):
   def wrapper(*args,**kwargs):
       print("装饰器开始运行了")
       print("装饰器捕获到的参数: " ,args,**kwargs)
       func(*args,**kwargs)
       print("装饰器结束运行了")

首先,我们在做传递调用的时候,wrapper应该调用形参来接收,接收后,再进行传递给函数func

来源:https://juejin.cn/post/7220939977245343802

标签:Python,装饰器
0
投稿

猜你喜欢

  • Python实现PING命令的示例代码

    2022-01-10 09:19:15
  • ASP/php/aspx生成静态网页的方法

    2007-10-25 12:01:00
  • asp金额大小写转换完全无错版

    2007-09-26 09:38:00
  • 分析Python的Django框架的运行方式及处理流程

    2022-03-13 14:47:50
  • SQL 2008 FileStream数据类型

    2008-10-28 21:07:00
  • Django实现翻页的示例代码

    2023-01-10 23:10:20
  • 如何对PHP程序中的常见漏洞进行攻击(下)

    2023-11-16 14:50:19
  • EXEC(EXECUTE)函数访问INSERTED或DELETED的内部临时触发表

    2012-01-29 18:07:30
  • wxPython中listbox用法实例详解

    2022-03-13 15:19:54
  • 分享6 个值得收藏的 Python 代码

    2022-12-31 05:09:57
  • CentOS 6.6服务器编译安装lnmp(Nginx1.6.2+MySQL5.6.21+PHP5.6.3)

    2023-11-15 06:40:50
  • asp入门之字符串函数介绍示例

    2008-11-04 20:18:00
  • Python搭建代理IP池实现接口设置与整体调度

    2023-05-25 11:52:03
  • Jquery实现div模拟Select控件

    2008-12-01 15:47:00
  • Python数据挖掘中常用的五种AutoEDA 工具总结

    2022-05-20 02:49:51
  • Python列表删除所有出现元素的两种方法

    2023-03-05 23:24:08
  • 对于Python中线程问题的简单讲解

    2023-12-28 10:16:31
  • 解决pycharm启动后总是不停的updating indices...indexing的问题

    2023-03-05 07:35:38
  • 扫盲大讲堂:SQL查询结果集对注入的影响及利用

    2009-09-05 09:49:00
  • Python 多线程知识点总结及实例用法

    2022-04-27 13:19:49
  • asp之家 网络编程 m.aspxhome.com