一文带你探寻Python中的装饰器
作者:真的不能告诉你我的名字 发布时间:2021-07-11 10:17:59
试官: 听说你熟悉python
,那么你能简单阐述一下python
的装饰器、生成器以及迭代器么?
我: emm, 我不清楚,我只是了解过python
最基本的代码。
上述是弟弟前段时间去面试运维开发,遇到的问题,emmm,运维是一个很杂的职业,在小公司,总结一句话就是宽而浅,痛定思痛,决定来了解一下python
特性,于是乎,就有了这篇文章。
这边文章,我们将介绍python
生成器,使用环境为: Python 3.6.8
什么是装饰器
要理解装饰器之前,我们需要了解什么是闭包函数。
闭包函数
我们简单写个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
装饰器,即:
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()
上面代码,我们定义了一个装饰器foo
,foo
需要传入一个函数, 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发送邮件到指定的邮箱。下面话不多说了,来一起看看详细的实现过程吧一、爬取天气
- 前言如果你有阅读源码的习惯,可能会看到一些优秀的代码经常出现带有 “with” 关键字的语句,它通常用在什么场景呢?今天就来说说 with
- windows下安装Virtualenvwrapper我们可以使用Virtualenvwrapper来方便地管理python虚拟环境,但是在
- 导语哈喽铁汁们好久不见吖~小编已经复工了于是马不停蹄赶来给大家准备新年礼物算开工礼物吧!海龟来作图虎年就是要画老虎2022不用纸和笔~今晚画
- 编写tasks.pyfrom celery import Celeryfrom tornado.httpclient import HTTP
- vue更新到2.0之后,作者就宣告不再对vue-resource更新,而是推荐使用axios。前段时间第一次在项目里用到vue,关于登陆问题
- 1.用于简单的对象检测、跟踪2.简单前背景分割#encoding:utf-8#黄色检测import numpy as npimport ar
- 本教程旨在介绍如何使用七牛的Python SDK来快速地进行文件上传,下载,处理,管理等工作。安装首先,要使用Python的SDK必须要先安
- 今天使用pip安装第三库时,有时会报错:pip._vendor.urllib3.exceptions.ReadTimeoutError: H
- 一、开始之前必须安装itchat库pip install itchat(使用pip必须在电脑的环境变量中添加Python的路径)或 cond
- python语言的一大优势:为科学计算提供了大量的支持功能,math模块提供了很多数学计算函数。math模块定义了一些数学模块,这个模块属于
- 如下所示:import osimport sysimport timeprocessNmae = 'parent'print
- 可以,具体说明和代码见下: <%@ Language=VBScript %><%Option
- 本文实例讲述了Python使用scrapy采集数据过程中放回下载过大页面的方法。分享给大家供大家参考。具体分析如下:添加以下代码到setti
- 许多人在数据科学、机器学习、web开发、脚本编写和自动化等领域中都会使用Python,它是一种十分流行的语言。Python流行的部分原因在于
- Python 内置的四种常用数据结构:列表(list)、元组(tuple)、字典(dict)以及集合(set)。这四种数据结构一但都可用于保
- 前言:图像滤波是图像处理和计算机视觉中最常用、最基本的操作。主要是去除图像中的噪声,因为图像平滑处理过程中往往会使得图像变的模糊,因此又叫模
- 通用用法但上图的字段名,类型需要根据不同接口填写,如某服务接口:因而对应的上传代码如下:# 输出参数:请求响应报文import reques
- 配置日志在Django中,可以通过logging模块来记录日志。日志记录器是将日志消息传递给日志处理器的对象。当需要记录日志时,可以使用以下
- 最近对H1的讨论很多(在文章内容页中),大致有以下两种情况:H1应该用于文章的标题上H1应该用于站点的标题上相信大多数人都偏向第一种方式:用