python装饰器源代码_13-Python-装饰器

1、装饰器的定义

装饰器的本质就是函数,用来装饰其它函数,就是为其它函数添加附加功能。

装饰器原则如下:

不能修改被装饰的函数的源代码

不能修改被装饰的函数的调用方式

2、实现装饰器知识储备

函数即变量

1 defbar():2 print("in the bar")3 deffoo():4 print("in the foo")5 bar()6

7 foo()8

9 print("----------分割线-----------")10

11 deffoo1():12 print("in the foo")13 bar1()14 defbar1():15 print("in the bar")16

17 foo1()18

19 print("----------分割线-----------")20 #这样会报错

21 #def foo2():

22 #print("in the foo")

23 #bar2()

24 #foo2()

25 #def bar2():

26 #print("in the bar")

高阶函数

把一个函数名当作实参传递给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能)

1 importtime2

3 defbar1():4 time.sleep(3)5 print("in the bar")6

7 deftest2(func):8 start_time =time.time()9 func() #相当于运行bar1()

10 stop_time =time.time()11 print("total run time %s" %(stop_time -start_time))12

13 test2(bar1)

返回值中包含函数名(不能修改函数的调用方式)

1 defbar2():2 time.sleep(3)3 print("in the bar2")4

5 deftest3(func):6 print(func)7 returnfunc8

9 print(test3(bar2)) #获取的是内存地址

10

11 res =test3(bar2)12 res()

嵌套函数

1 deffoo():2 print("in the foo")3 defbar():4 print("in the bar")5 bar() #局部变量只能在其作用域内调用

6

7 foo()

1 x =02 defgrandpa():3 x = 1

4 defdad():5 x = 2

6 defson():7 x = 3

8 print(x) #最终打印结果为3

9 son()10 dad()11 grandpa()

高阶函数 + 嵌套函数 --》装饰器

1 importtime2

3

4 deftimer(func):5 defdeco():6 start_time =time.time()7 func()8 stop_time =time.time()9 print("total time is %s" % (stop_time -start_time))10 return deco #返回deco()的内存地址

11

12

13 deftest1():14 time.sleep(3)15 print("in the test1")16

17

18 deftest2():19 time.sleep(3)20 print("in the test2")21

22 #以下可直接用装饰器语法代替

23 timer(test1) #test1的内存地址赋值给func,返回deco()的内存地址

24 print(timer(test1)) #返回deco()的内存地址

25 test1 = timer(test1) #内存地址赋值给test1

26 test1() #相当于执行deco()

27

28 timer(test2)29 test2 =timer(test2)30 test2()31

32 print("---------我是分隔符---------")33

34

35 #装饰器语法如下。(和上面引用的效果一样)

36 @timer #相当于test1 = timer(test1)

37 deftest1():38 time.sleep(3)39 print("in the test1")40

41

42 @timer #相当于test1 = timer(test1)43 deftest2():44 time.sleep(3)45 print("in the test2")46

47

48 test1()49 test2()

3、动态参数装饰器

deftimer(bar):def inner(*args, **kwargs):

start_time=time.time()

foo_ret= bar(*args, **kwargs)

end_time=time.time()

used_time= end_time -start_timeprint(used_time)returnfoo_retreturninner

@timerdef foo(*args, **kwargs):

time.sleep(1)print("我的参数:", args, kwargs)print("我的运行时间:")return "我的返回值"ret= foo("动态参数装饰器", (1, 2), name="Druid", age=18)print(ret)

输出结果如下:

1255010-20190313104040265-655976853.png

4、装饰器原理图解

4.1 被装饰函数没有返回值

1255010-20190312160147209-431523247.png

4.2 被装饰函数有返回值

1255010-20190312170908140-1437679650.png

注意:第二步仅为过程分析量,不作为真实的执行顺序。

5、装饰器固定格式

装饰器的固定格式如下例所示:

defwrapper(func):"""该函数为装饰器函数

:param func: 这里的func参数实质是指向被装饰函数的内存地址

:return:"""

def inner(*args, **kwargs):"""该函数为装饰器函数内部函数

:param args: 实质接收的是被装饰函数的位置参数

:param kwargs: 实质接收的是被装饰函数的关键字参数

:return: 返回的是被装饰函数的返回值"""

print("这里放被装饰函数执行之前要做的事")

func_ret= func(*args, **kwargs) #被装饰的函数

print("这里放被装饰函数执行之后要做的事")returnfunc_retreturninner

@wrapper#等价于my_func = wrapper(my_func)

def my_func(*args, **kwargs):"""该函数为被装饰函数

:param args: 接收位置参数

:param kwargs: 接收关键字参数

:return: 返回值"""

print(*args, **kwargs)returnret

ret= my_func() #执行原函数,实质是执行inner()。函数返回值保存在变量ret中。

6、装饰器修复

当我们使用装饰器去装饰某个函数时,我们想要引用被装饰函数原私有属性,如__name__、__doc__时,就有问题了,因为我们虽然仍然在执行被装饰函数,但其实执行的是闭包,看下例。

defwrapper(func):"""该函数为装饰器函数

:param func: 这里的func参数实质是指向被装饰函数的内存地址

:return:"""

def inner(*args, **kwargs):"""该函数为闭包(装饰器函数内部函数)

:param args: 实质接收的是被装饰函数的位置参数

:param kwargs: 实质接收的是被装饰函数的关键字参数

:return: 返回的是被装饰函数的返回值"""func_ret= func(*args, **kwargs) #被装饰的函数

returnfunc_retreturninner

@wrapper#等价于my_func = wrapper(my_func)

def my_func(*args, **kwargs):"""该函数为被装饰函数

:param args: 接收位置参数

:param kwargs: 接收关键字参数

:return: 返回值"""

print(*args, **kwargs)return "返回值"my_func("装饰器没被修复前,被装饰函数原函数的私有属性如__name__、__doc__是获取不到的,如下:") #执行原函数,实质是执行inner()

print(my_func.__name__) #打印函数的名字

print(my_func.__doc__) #打印函数的注释文档

输出结果如下:

1255010-20190313112535066-646097149.png

如果仍想使用被装饰函数的原私有属性,那么就可以用装饰器修复:

from functools importwrapsdefwrapper(func):"""该函数为装饰器函数

:param func: 这里的func参数实质是指向被装饰函数的内存地址

:return:"""@wraps(func)def inner(*args, **kwargs):"""该函数为闭包(装饰器函数内部函数)

:param args: 实质接收的是被装饰函数的位置参数

:param kwargs: 实质接收的是被装饰函数的关键字参数

:return: 返回的是被装饰函数的返回值"""func_ret= func(*args, **kwargs) #被装饰的函数

print("装饰器修复不会改变原装饰器的作用")returnfunc_retreturninner

@wrapper#等价于my_func = wrapper(my_func)

def my_func(*args, **kwargs):"""该函数为被装饰函数

:param args: 接收位置参数

:param kwargs: 接收关键字参数

:return: 返回值"""

print(*args, **kwargs)return "返回值"my_func("装饰器被修复后,被装饰函数原函数的私有属性如__name__、__doc__就可以正常获取了,如下:") #执行原函数,实质是执行inner()

print(my_func.__name__) #打印函数的名字

print(my_func.__doc__) #打印函数的注释文档

输出结果如下:

1255010-20190313142036803-1767792186.png

7、带参数的装饰器

需求:很多函数共用一个装饰器,要求随时可以关闭装饰器功能,且尽可能的减少代码修改。 该需求可以用标记位来实现,如下:

importtime

FLAG=Truedeftimmer_out(flag):deftimmer(func):def inner(*args, **kwargs):ifflag:

start_time=time.time()

ret_func= func(*args, **kwargs)

end_time=time.time()

used_time= end_time -start_timeprint("函数{name}执行时间:{time}".format(name=func.__name__, time=used_time))#print("函数{name}执行时间:{time}".format_map({"name": func.__name__, "time": used_time}))

else:

ret_func= func(*args, **kwargs)returnret_funcreturninnerreturntimmer

@timmer_out(FLAG)#第一步,先执行timmer_out(FLAG),得到返回值timmer。第二步执行@timmer,即 my_func1 = timmer(my_func1)

defmy_func1():

time.sleep(1)print("my_func1")

@timmer_out(FLAG)defmy_func2():

time.sleep(1)print("my_func2")

@timmer_out(FLAG)defmy_func3():

time.sleep(1)print("my_func3")

my_func1()

my_func2()

my_func3()

当FLAG置为True时,装饰器功能生效,输出结果如下图所示:

1255010-20190313151604850-2139918969.png

当FLAG置为False时,装饰器功能关闭,输出结果如下图所示:

1255010-20190313151707252-536390938.png

8、多个装饰器装饰一个函数

defwrapper1(func):definner1():print("wrapper1, before func")

func()print("wrapper1, after func")returninner1defwrapper2(func):definner2():print("wrapper2, before func")

func()print("wrapper2, after func")returninner2

@wrapper2

@wrapper1defmy_func():print("function is my func")

my_func()

注意输出结果:

1255010-20190313163737497-130606014.png

为什么结果是这样?请看如下分析:

1255010-20190313172810631-679930933.png

为什么是先执行@wrapper1而不是@wrapeer2呢?因为装饰器在找到被装饰函数会优先执行。

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

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

相关文章

算法设计与分析——分治与递归策略——hanoi问题

**汉诺塔问题:**古代有一个梵塔,塔内有三个座A、B、C,A座上有64个盘子,盘子大小不等,大的在下,小的在上(如图)。有一个和尚想把这64个盘子从A座移到B座,但每次只能允许移…

post多个参数_关于HTTP GET和POST的区别

Photo by Luca Bravo on UnsplashGET还是POST? 考虑将浏览器作为客户端,可以缓存哪种方法? 哪个是"安全"方法? 哪一个不是幂等的? 如果我将端点URL复制并粘贴到浏览器的地址栏中,然后按Enter&…

小试YARP

.net core下,一个轻量组反向代理库,由微软发起。做了一个简单的带验证的反向代理,应用结构如上图,一个验证服务,两个业务服务和一个YARP服务。源码https://github.com/axzxs2001/Asp.NetCoreExperiment/tree/master/As…

二分法——leetcode35. 搜索插入位置

二分法的第一种写法 在左闭右闭的区间寻找target&#xff1b; 不变的量是&#xff1a;[left,right]; #include<bits/stdc.h> using namespace std; int binary_search(vector<int> &nums,int target) {int left 0;int right nums.size() - 1; //定义target…

python大鱼吃小鱼_python 游戏编程 大鱼吃小鱼

# 游戏编程&#xff1a;按照以下游戏编写一个乌龟类和鱼类&#xff0c;并尝试编写游戏。 # 假设游戏场景&#xff08;x&#xff0c;y&#xff09;为0<x<10,0<y<10 # 游戏生成1只乌龟和10只鱼 # 他们的移动方向均随机 # 乌龟的最大移动速度为2&#xff0c;它可以随机…

Entity Framework Core 5中实现批量更新、删除

本文介绍了一个在EntityFramework Core 5中不需要预先加载数据而使用一句SQL语句批量更新、删除数据的开发包&#xff0c;并且分析了其实现原理&#xff0c;并且与其他实现方案做了比较。一、背景随着微软全面拥抱开源&#xff0c;.Net开源社区百花开放&#xff0c;涌现了非常多…

篮子里拿鸡蛋问题

一个一个拿&#xff0c;正好拿完。两个两个拿&#xff0c;还剩一个。三个三个拿&#xff0c;正好拿完。 四个四个拿&#xff0c;还剩一个。五个五个拿&#xff0c;还差一个。六个六个拿&#xff0c;还剩三个。 七个七个拿&#xff0c;正好拿完。八个八个拿&#xff0c;还剩一个…

python 复制文件_python 复制文件

展开全部 用Python把某一2113目录下的文件复制到指定5261目录中&#xff0c;代码如下&#xff1a;4102 1、首先插入必要的库&#xff1a;import os import os.path import shutil import time, datetime 2、实现复制文1653件代码如下&#xff1a;def copyFiles(sourceDir,targe…

一套标准的ASP.NET Core容器化应用日志收集分析方案

点击上方蓝字给一个关注吧讲故事关注我公众号的朋友&#xff0c;应该知道我写了一些云原生应用日志收集和分析相关的文章&#xff0c;其中内容大多聚焦某个具体的组件&#xff1a;超级有用的TraceId&#xff0c;快点用起来吧&#xff01;如何利用NLog输出结构化日志&#xff0c…

算法设计与分析——递归与分治策略——棋盘覆盖

问题描述 棋盘覆盖问题要求在2^k * 2^k 个方格组成的棋盘中&#xff0c;你给定任意一个特殊点&#xff0c;用一种方案实现对除该特殊点的棋盘实现全覆盖。 建立模型如图&#xff1a; 解决方案就是利用分治法&#xff0c;将方形棋盘分成4部分&#xff0c;如果该特殊点在某一部…

函数求值需要运行所有线程_JavaScript函数式编程(二)

纯函数就是&#xff0c;对于相同的输入&#xff0c;永远会得到相同的输出&#xff0c;而且没有任何可观察的副作用&#xff0c;也不依赖外部环境的状态但是实际的编程中&#xff0c;特别是前端的编程范畴里&#xff0c;“不依赖外部环境”这个条件是根本不可能的&#xff0c;我…

如何在 C# 中使用 RabbitMQ

译文链接&#xff1a;https://www.infoworld.com/article/3200210/how-to-work-with-rabbitmq-in-c.htmlRabbitMQ 是一个非常流行的&#xff0c;开源的&#xff0c;使用Erlang语言编写的框架&#xff0c;通常在电信级平台中作为消息中间件使用&#xff0c;RabbitMQ实现了高级的…

算法设计与分析——递归与分治——归并排序

归并排序采用的是一种分治的思想&#xff0c;如下图&#xff0c;先将要排序的元素分为两块&#xff0c;每个块又开始分裂&#xff0c;然后逐个按照特定顺序合并&#xff0c;合成最后我们需要的数组。 归并排序的复杂度&#xff1a; 时间复杂度&#xff1a;O(nlogn) 空间复杂度&…

git 回退上一个版本_Git小白使用教程:详细、显现、真正手把手教!

不少小伙伴私信问我GitHub怎么使用&#xff1f;今天更一下&#xff0c;希望能帮到你&#xff0c;有问题评论区拍砖交流吧。

在传统行业做数字化转型之业务篇

【数字化转型】| 作者 / Edison Zhou这是EdisonTalk的第307篇原创内容在过去的两年时间里&#xff0c;我加入了一家传统行业的企业参与其数字化转型的过程&#xff0c;现在我将我的经历分享出来&#xff0c;本文是第三部分—业务篇&#xff0c;主要会介绍一下传统企业通用的三大…

算法设计与分析——递归与分治策略——快速排序

快速排序——递归算法 处理i,j的先后顺序不能改变 快速排序的基本思想&#xff1a;通过一趟排序将待排记录分隔成独立的两部分&#xff0c;其中一部分记录的关键字均比另一部分的关键字小&#xff0c;则可分别对这两部分记录继续进行排序&#xff0c;以达到整个序列有序。 函数…

git pull 覆盖本地_SVN与Git比较的优缺点差异

一、 集中式vs分布式1. Subversion属于集中式的版本控制系统集中式的版本控制系统都有一个单一的集中管理的服务器&#xff0c;保存所有文件的修订版本&#xff0c;而协同工作的人们都通过客户端连到这台服务器&#xff0c;取出最新的文件或者提交更新。Subversion的特点概括起…

C#阻塞队列BlockingCollection

BlockingCollection是一个比较冷门的类&#xff0c;我们先看下官方对这个类的定义&#xff1a;简单来说&#xff0c;BlockingCollection就是一个线程安全的阻塞队列&#xff0c;利用阻塞这个特性&#xff0c;我们可以实现进程内的生产者-消费者模式&#xff0c;比如消息转发、日…

算法设计与分析——递归与分治策略——线性时间选择

顾名思义&#xff1a;这篇文章讲解的就是如果用线性时间算法来作出元素选择问题。 问题描述&#xff1a;给定线性序集中n个元素和一个整数k&#xff0c;1<k<n.要求找出这n个元素中第k小的元素&#xff0c;即如果将这个n个元素依其线性序排列时&#xff0c;排在第k个位置的…

如何在 .NET 中使用 Redis缓存

译文链接&#xff1a;https://www.infoworld.com/article/3187905/how-to-work-with-redis-cache-in-net.html缓存是一种状态管理机制&#xff0c;通常用于提升你的应用程序性能&#xff0c;它很大程度上能够减少一个请求对你系统资源的消耗。Redis是一个开源的&#xff0c;高性…