python装饰器函数执行后日志_python 装饰器理解

在理解装饰器之前,先应该对闭包有个概念:所谓闭包,就是将组成函数的语句和这些语句的执行环境打包在一起时得到的对象,它的主要作用是封存上下文。这一特性可以巧妙的被用于现有函数的包装,从而为现有函数添加功能,这就是装饰器。

装饰器的本质与作用

装饰器(Decorator)的本质是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。

它经常用于有切面需求的场景 ,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续复用。

我们先看一个例子,代码如下:1

2

3

4#!/usr/bin python

deffoo():

print('i am foo')

现在有一个新的需求,希望可以记录下函数的执行日志,于是在代码中添加了日志代码:1

2

3deffoo():

print('i am foo')

logging.info("foo is running")

此时bar()、bar2()也有类似的需求,再写一个logging在bar函数里?为了提高代码的复用,我们重新定义一个函数:专门处理日志,日志处理完后再执行真正的业务代码:1

2

3

4

5

6

7

8

9

10#!/usr/bin python

defuse_logging(func):

logging.warn("%s is running"%func.__name__)

func()

defbar():

print('i am bar')

use_logging(bar)

上述代码虽然解决了,但是我们每次都要将一个函数作为参数传递给use_logging函数,而且这种方式以已经破坏了原有的代码逻辑结构,本来的业务逻辑是执行bar(),但是现在不得已改成执行use_logging(bar)。鉴于这个弊端,Python装饰器应运而生。

简单装饰器

函数use_logging就是装饰器,它把执行真正业务方法的func包裹在函数里面,看起来像bar被use_logging装饰了。1

2

3

4

5

6

7

8

9

10

11defuse_logging(func):

defwrapper(*args,**kwargs):

logging.warn("%s is running"%func.__name__)

returnfunc(*args,**kwargs)

returnwrapper

defbar():

print('i am bar')

bar=use_logging(func)

bar()

在这个例子中,函数进入和退出时,被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。

但是bar = use_logging(func)这样的写法未免太过麻烦,于是Python提供了一种更优雅的写法:语法糖。@符号是装饰器的语法糖,在定义函数时使用,避免了再一次赋值操作。1

2

3

4

5

6

7

8

9

10

11

12

13

14defuse_logging(func):

defwrapper(*args,**kwargs):

logging.warn("%s is running"%func.__name__)

returnfunc(*args)

returnwrapper

@use_logging# 语法糖,等价于:bar=use_logging(bar)

deffoo():

print('i am foo')

@use_logging

defbar():

print('i am bar')

bar()

如上所示,直接调用bar()就可以获得结果。如果有其他类似函数,也可以继续调用装饰函数,而不用重复修改或增加新的封装。

装饰器在Python使用如此方便都要归因于Python函数能像普通的对象(Python中一切皆对象)一样作为参数传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以定义在另一个函数内。

带参数的装饰器

在上述的的装饰器调用中,@use_logging该装饰器唯一的参数就是执行业务的函数,而装饰器的语法允许我们在调用时,提供其他参数,比如:@decorator(a),这样,就为装饰器的编写和使用提供了更大的灵活性。1

2

3

4

5

6

7

8

9

10

11

12

13

14defuse_logging(level):

defdecorator(func):

defwrapper(*args,**kwargs):

iflevel=="warn":

logging.warn("%s is running"%func.__name__)

returnfunc(*args)

returnwrapper

returndecorator

@use_logging(level="warn")

deffoo(name='foo')

print("i am %s"%name)

foo()

上述的use_logging是允许带参数的装饰器,是对原有装饰器的一次函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包,当我们调研这个语法糖的时候,Python会发现这一层的封装,并将参数传递到装饰器的环境中。

总结:无参的装饰器参数是要装饰的函数;有参装饰器参数是函数的参数,最后返回的是内部函数。

类装饰器

相比函数装饰器,类装饰器具有高内聚、灵活性大、高封装等优点。使用类装饰器还可以依靠类内部的__call__方法,当使用@将装饰器附加到函数上时 ,就会调用此方法。1

2

3

4

5

6

7

8

9

10

11

12

13classfoo(object):

def__init__(self,func):

self._func=func

def__call__(self):

print('class decorator running')

self._func()

print('class decorator ending')

@foo

defbar():

print('i am bar')

bar()

使用类装饰器极大地复用了代码,但是它也存在缺陷:原函数的元信息不见了,比如函数的docstring、__name__、参数列表,我们先写一个装饰器:1

2

3

4

5deflogged(func):

defwith_logging(*args,**kwargs):

printfunc.__name__+"was called"

returnfunc(*args,**kwargs)

returnwith_logging

定义一个函数来调用该装饰器:1

2

3

4@logged

deff(x):

"""does some math"""

returnx+x*x

上述函数完全等价于:1

2

3

4deff(x):

"""does some math"""

returnx+x*x

f=logged(f)

可以看出,函数f被with_logging取代了,因此它的docstring、__name__也就变成了with_logging函数的信息了:1

2printf.__name__# print 'with_logging'

printf.__doc__# print None

好在我们有functools.wraps(Python的一个模块),wraps本身就是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中,使得装饰器函数也有和原函数一样的元信息。1

2

3

4

5

6

7

8

9

10

11

12

13

14

15# 导入functools模块中的wraps装饰器

fromfunctoolsimportwraps

deflogged(func):

@wraps(func)

defwith_logging(*args,**kwagrs):

printfunc.__name__+"was called"

returnfunc(*args,**kwargs)

returnwith_logging

@logged

deff(x):

"""does some math"""

returnx+x*x

printf.__name__# print 'f'

printf.__doc__# print 'does some math'

内置装饰器

在Python中有三个内置的装饰器,都与class相关:

1)staticmethod:类静态方法,其根跟成员方法的区别是没有self参数,并且可以在类不进行实例化的情况下调用。

2)classmethod:与成员方法的区别在于所接收的第一个参数不是self(类实例的指针),而是cls(当前类的具体类型)。

3)property:属性的意思,表示可以通过类实例直接访问的信息。1

2

3

4

5

6

7

8

9

10

11

12

13

14

15classTest(object):

def__init__(self,name):

self._name=name

@staticmethod

defnewTest1(name):

returnTest(name)

@classmethod

defnewTest2(cls):

returnTest('')

@property

defname(self):

returnself._name

装饰器的调用顺序

装饰器是可以叠加使用的,那么这就涉及到装饰器的调用顺序。对于Python中的“@”语法糖,装饰器的调用顺序与使用@语法糖的声明顺序相反。1

2

3

4

5

6

7

8

9# 装饰器的声明顺序

@a

@b

@c

deff():

pass

# 等效于

f=a(b(c(f)))# 以c、b、a的顺序调用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/470559.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二十、PHP框架Laravel学习笔记——模型的作用域

一.本地作用域 很多情况下,我们在数据查找时有一部分条件会被重复且大量使用;而这个条件,可能只是在这个模型对应的数据表使用,别的表并不使用;那么这种情况,可以使用本地作用域的方式&#xf…

App接口如何保证安全

微信开发或者高德地图,百度地图什么的api要使用,使用之前都需要注册一个账号,然后系统会给你一个key,然后调用api的时候把key传给服务器。 平常公司内部开发项目时,直接用mvc为app客户端提供接口,没有做过验…

论文阅读 - Joint Beat and Downbeat Tracking with Recurrent Neural Networks

文章目录1 概述2 信号预处理3 分类神经网络4 动态贝叶斯网络(HMM)4.1 原始的bar pointer model4.2 原始的bar pointer model的缺点4.3 改进后的模型5 预测参考资料1 概述 最近在做音乐卡点相关的项目,需要对音乐的基本特征进行理解&#xff…

二十一、PHP框架Laravel学习笔记——模型的访问器和修改器

一.访问器 访问器:就是在获取数据列表时,拦截属性并对属性进行修改的过程;比如,我们在输出性别时,在性别左右加上括号,或给邮件转换为大写; //访问器,前固定 get&#…

python将txt转json_Python控制乐高EV3,以及VSCODE环境配置

乐高EV3的可扩展性很强,但如何用pc连接ev3,并用python代码来控制EV3,资料太少了,试着做了一次,记录在这里。需要的硬/软件硬件准备一、乐高EV3二、PC,win10系统三、TF卡(我用的是64G的&#xff…

移动文件读/写指针----lseek

头文件&#xff1a;#include<sys/types.h>、#include<unistd.h> 函数原型&#xff1a;off_t lseek(int fildes,off_t offset,int whence) 参数说明&#xff1a;fildes:文件描述符 offset:偏移量&#xff0c;正数表示正向偏移&#xff0c;负数表示负向偏移。 whence…

Yolo系列知识点梳理(Yolov1-v5)

文章目录1 概述2 Yolo系列模型2.1 基石 - Yolov12.1.1 Yolov1的网络结构2.1.2 Yolov1的feature map2.1.3 Yolov1的训练2.1.4 Yolov1的预测2.1.5 Yolov1小结2.2 Yolo9000 - Yolov22.2.1 Better2.2.1.1 引入了Batch normalization2.2.1.2 高分辨率的分类器2.2.1.3 加入了anchor机…

二十二、PHP框架Laravel学习笔记——集合的使用

一&#xff0e;创建集合 什么是集合&#xff1f;即&#xff1a;它是一种更具读取性和处理能力的数组封装&#xff1b;比如&#xff0c;我们从数据库得到的数据列表&#xff0c;它就是一种集合&#xff1b;数据集合&#xff0c;提供了大量的方法方便我们进行各种操作&#xff1…

神经网络用python还是matlab_Matlab与神经网络学习笔记

这是学习《Neural Network and Deep Learning》的笔记。在刚开始学习时&#xff0c;电脑安装的是python3&#xff0c;而作者用的是2.我想着反正要熟悉代码&#xff0c;大学期间MATLAB用的比较多&#xff0c;也懒得去折腾python2或是重写成python3.于是用MATLAB完成了里面的实例…

二十三、PHP框架Laravel学习笔记——集合的常用方法

一&#xff0e;常用方法 all()方法&#xff0c;转换为属性形式输出&#xff0c;使用 dd 方法看类型&#xff1b; $collection collect([1, 2, 2, 3, 4, 4, 4]); dd($collection->all()); PS&#xff1a;$collection->dd()方法可以以 dd()模式输出&#xff0c;还有 du…

mac看图软件哪个好用_细数Mac上那些好用且免费的软件(三)

许多朋友购买了Mac电脑之后发现很多软件都没有&#xff0c;而且苹果商店好多软件都是收费的。那有没有免费的软件也能满足日常的需求呢&#xff1f;macw小编就为大家推荐一些免费且超级好用的软件&#xff0c;赶快来看看有没有你需要的吧&#xff01;细数Mac上那些好用且免费的…

图像表格实线和虚线检测

文章目录1 背景简述2 camelot中的方法2.1 二值化2.2 腐蚀膨胀2.3 轮廓检测2.4 结果展示3 基于霍夫直线检测的方法3.1 霍夫直线检测原理3.2 概率霍夫直线检测3.3 霍夫直线应用参考资料1 背景简述 图像中的表格结构化是一个比较热门的话题&#xff0c;其输入是一张图片&#xff…

二十四、PHP框架Laravel学习笔记——模型的数据集合

一&#xff0e;数据集合 数据集合&#xff0c;就是已经将模型方法 get()获取到的数据再进行处理&#xff1b;比如&#xff1a;map()方法&#xff0c;通过它可以实现类似访问器一样对字段进行处理的效果&#xff1b; $users User::get(); //使用集合方法 map 可以对输出的字…

const参数,const返回值与const函数

在C程序中&#xff0c;经常用const 来限制对一个对象的操作&#xff0c;例如&#xff0c;将一个变量定义为const 的&#xff1a; const int n3; 则这个变量的值不能被修改&#xff0c;即不能对变量赋值。 const 这个关键字经常出现在函数的定义中&#xff0c;而且会出现在不同…

python简笔画怎么画_怎样用tk语句在Python下画一棵树

展开全部 1.代码的结2113构&#xff1a; 本代码有两个子5261函数组成&#xff0c;据图有main函数4102和画树函数组成。 2.编写画1653树函数&#xff1a; 画树函数&#xff0c;就是用来画出我们的树的一种子函数&#xff0c;代码如下&#xff1a;def tree(plist,l,a,f): if l>…

论文阅读 - AUTOVC: Zero-Shot Voice Style Transfer with Only Autoencoder Loss

文章目录1 概述2 模型架构3 模块解析3.1 获取梅尔频谱3.2 speaker encoder3.3 AutoVC3.4 Vocoder4 关键部分参考资料1 概述 voice conversion这个任务的目标是输入两个音频&#xff0c;其输入是两段音频&#xff0c;一段音频称为content_audio&#xff0c;另一段称为speaker_a…

二十五、PHP框架Laravel学习笔记——模型的一对一关联

一&#xff0e;关联概念 关联模型&#xff0c;即&#xff1a;两张或以上的表进行一定规则的绑定关联&#xff1b;比如&#xff1a;一个学生(学生表)对应一张个人信息卡(信息表)&#xff0c;这种就是一对一&#xff1b;再比如&#xff1a;一篇博文(帖子表)对应多个评论(评论表)…

linux-0.11内核 调试教程+GCC源代码

http://pan.baidu.com/share/home?uk453348606&viewshare#category/type0 http://blog.csdn.net/oldlinux/article/details/42558445转载于:https://www.cnblogs.com/zengkefu/p/5563351.html

python 私有属性_Python3伪私有属性

1&#xff1a;伪私有属性介绍 在Python中一切都是对象&#xff0c;并且类的所有属性和方法都是公有的(public)&#xff0c;并不存在private 关键字来声明私有方法或属性。那么在Python中怎么实现像其他编程语言一样的私有属性呢&#xff1f;其实只需在属性前加上双下划线(但是结…

小工具:基于颜色的视频和图片切割

文章目录1 前言2 方案简述3 效果1 前言 最近做一个短视频相关的项目的时候&#xff0c;发现输入的视频有很多是有黑边的&#xff0c;有些可能是白边或者其他颜色的边。这对下游的模型处理有很大的影响。于是就写了一个自动判断填充边的颜色&#xff0c;并根据该颜色自动切割视…