这是我见过最全面的Python装饰器教程了
liebian365 2024-10-24 14:31 22 浏览 0 评论
装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)。在程序开发中经常使用到的功能,合理使用装饰器,能让我们的程序如虎添翼。
1. 函数名应用
函数名是什么?函数名是函数的名字,本质:变量,特殊的变量。
1 ) 函数名就是函数的内存地址,直接打印函数名,就是打印内存地址
def func1(): print(123) print(func1) # <function func1 at 0x0000029042E02E18>
2 ) 函数名可以作为变量
def func1(): print(111) f = func1 f() # f() 就是func1()
3 ) 函数名可以作为函数的参数
def func1(): print(111) def func2(x): x() func2(func1) #func1作为func2的参数
4 ) 函数名可以作为函数的返回值
def wrapper(): def inner(): print('inner') return inner f = wrapper() f()
5 ) 函数名可以作为容器类类型的元素
使用for循环批量执行函数 def func1(): print('func1') def func2(): print('func2') def func3(): print('func3') l1 = [func1,func2,func3] for i in l1: i()
像上面函数名这种,叫做第一类对象。
第一类对象( first-class object)指:
1.可在运行期创建
2.可用作函数参数或返回值
3.可存入变量的实体
*不明白?那就记住一句话,就当普通变量用
2. 闭包
1、 闭包函数 : 内部函数包含对外部作用域而非全局作用域变量的引用,该内部函数称为闭包函数
2、闭包的作用:爬虫、装饰器
当程序执行遇到函数执行时,会在内存空间开辟局部命名空间,当函数执行完毕,该命名空间会被销毁。但是如果这个函数内部形成闭包,则该内存空间不会随着函数执行完而消失。
3、如何判断是否是闭包:print(函数名.__closure__) 结果是 cell 说明是闭包,结果是 None 说明不是闭包。
闭包举例
def wrapper(): name = 'summer' def inner(): print(name) inner() wrapper() # summer
如何判断它是否是一个闭包函数呢? 内层函数名.__closure__ cell 就是=闭包
例 1.
def wrapper(): name = 'summer' def inner(): print(name) inner() print(inner.__closure__) wrapper() 执行输出: summer (<cell at 0x0000017FC9C90B58: str object at 0x0000017FCA349AD0>,)
例 2.
name = 'summer' def wrapper(): def inner(): print(name) inner() print(inner.__closure__) wrapper() 结果输出: summer None
返回值为None 表示它不是闭包,因为name是一个全局变量,如果函数调用了外层变量而非全局变量,那么它就是闭包。
例 3.
name = 'summer' def wrapper2(): name1 = 'spring' def inner(): print(name) print(name1) inner() print(inner.__closure__) wrapper2() 结果输出: summer spring (<cell at 0x030B7310: str object at 0x03043680>,)
只要引用了外层变量至少一次,非全局的,它就是闭包
例 4:判断 下面的函数,是一个闭包吗? ******
name = 'summer' def wraaper2(n): #相当于n = 'summer' def inner(): print(n) inner() print(inner.__closure__) wraaper2(name) 结果输出: summer (<cell at 0x03867350: str object at 0x037F3680>,)
它也是一个闭包. 虽然wraaper2传了一个全局变量,但是在函数wraaper2内部,inner引用了外层变量,相当于在函数inner外层定义了 n = 'summer',所以inner是一个闭包函数
闭包的好处 : 当函数开始执行时,如果遇到了闭包,他有一个机制,他会永远开辟一个内存空间,将闭包中的变量等值放入其中,不会随着函数的执行完毕而消失。
举一个例子:爬3次,内存开了3次,很占用内存
from urllib.request import urlopen content1 = urlopen('https://www.cnblogs.com/').read().decode('utf-8') content2 = urlopen('https://www.cnblogs.com/').read().decode('utf-8') content3 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')
把它封装成闭包
from urllib.request import urlopen def index(): url = "https://www.cnblogs.com/" def get(): return urlopen(url).read() return get #return的是get,就是一个函数名 cnblog = index() print(cnblog) # <function index.<locals>.get at 0x02F46978> content = cnblog() print(content) # 页面源码
这个例子,只有第一遍,是从网站抓取的。之后的执行,直接从内存中加载,节省内存空间
3.装饰器
3.1 装饰器初识
装饰器本质: 就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。
装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。
import time def timmer(f): def inner(): start_time = time.time() f() end_time = time.time() print('此函数的执行时间为{}'.format(end_time - start_time)) return inner def func1(): print('in func1') time.sleep(1) func1 = timmer(func1) print(func1) func1() # 这里的func1是全新的func1,就是上面的赋值,此时相当于执行 inner函数 输出结果: <function timmer.<locals>.inner at 0x03822DF8> in func1 此函数的执行时间为1.0003533363342285
代码从上至下执行
语法糖: 想测试谁,前面加@装饰器函数,即可。 写装饰器,约定俗成,函数名为wrapper
def wrapper(func): def inner(*args,**kwargs): '''被装饰函数之前''' ret = func(*args,**kwargs) '''被装饰函数之后''' return ret return inner @wrapper def func(*args,**kwargs): print(args,kwargs) return 666 print(func()) 输出结果: () {} 666
装饰器利用return制造了一个假象,func()执行,其实是执行inner() , func()把原来的func()给覆盖了
3.2 装饰器传参
例 1: 上面装饰器的例子,func1,要传2个参数a,b
import time def timmer(f): def inner(a,b): start_time = time.time() f(a,b) end_time = time.time() print('此函数的执行时间为{}'.format(end_time - start_time)) return inner @timmer def func1(a,b): print('in func1 {}{}'.format(a,b)) time.sleep(1) # 模拟程序逻辑 func1(1,2) 执行输出: in func1 12 此函数的执行时间为1.0006024837493896
例 2: 如果有多个参数呢?改成动态参数
import time def timmer(f): def inner(*args,**kwargs): start_time = time.time() f(*args,**kwargs) end_time = time.time() print('此函数的执行时间为{}'.format(end_time - start_time)) return inner @timmer def func1(*args,**kwargs): print('in func1 {}{}'.format(args,kwargs)) time.sleep(1) # 模拟程序逻辑 func1(1,2,a='3',b=4) 执行输出: in func1 (1, 2){'b': 4, 'a': '3'} 此函数的执行时间为1.000101089477539
函数的执行时,*打散 ;
函数的定义时,*聚合。
from functools import wraps def wrapper(f): # f = func1 def inner(*args,**kwargs): #聚合,args (1,2,3) '''执行函数之前的相关操作''' ret = f(*args,**kwargs) # 打散 1,2,3 '''执行函数之后的相关操作''' return ret return inner @wrapper # func1 = wrapper(func1) func1 = inner def func1(*args): #args (1,2,3) 聚合 print(666) return args print(func1(*[1,2,3])) 执行输出: 666 (1, 2, 3)
例 3 *****
import time #1.加载模块 def timmer(*args,**kwargs): #2.加载变量 5.接收参数True,2,3 def wrapper(f): #6.加载变量 8.f = func1 print(args, kwargs) #9.接收timmer函数的值True,2,3 def inner(*args,**kwargs): #10.加载变量. 13.执行函数inner if flag: #14 flag = True start_time = time.time() #15 获取当前时间 ret = f(*args,**kwargs) #16 执行func1 time.sleep(0.3) #19 等待0.3秒 end_time = time.time() #20 获取当前时间 print('此函数的执行效率%f' % (end_time-start_time)) #21 打印差值 else: ret = f(*args, **kwargs) return ret #22 返回给函数调用者func1() return inner #11 返回给函数调用者wrapper return wrapper #7.返回给函数调用timmer(flag,2,3) flag = True #3 加载变量 @timmer(flag,2,3) # 4.执行函数timmer(flag,2,3) 17.执行函数func1 两步:1,timmer(flag,2,3) 相当于执行wrapper 2.@wrapper 装饰器 func1 = wrapper(func1) def func1(*args,**kwargs): return 666 #18 返回给函数调用者f(*args,**kwargs) print(func1()) #12 执行函数
写装饰器,一般嵌套3层就可以了
3.3 多个装饰器,装饰一个函数
def wrapper1(func): # func == f函数名 def inner1(): print('wrapper1 ,before func') # 2 func() print('wrapper1 ,after func') # 4 return inner1 def wrapper2(func): # func == inner1 def inner2(): print('wrapper2 ,before func') # 1 func() print('wrapper2 ,after func') # 5 return inner2 @wrapper2 # f = wrapper2(f) 里面的f==inner1 外面的f == inner2 @wrapper1 # f = wrapper1(f) 里面的f==函数名f 外面的f == inner1 def f(): # 3 print('in f') f() # inner2() 执行输出: wrapper2 ,before func wrapper1 ,before func in f wrapper1 ,after func wrapper2 ,after func
哪个离函数近,哪个先计算 。 最底下的先执行
执行顺序如下图:
多个装饰器,都是按照上图的顺序来的
4. 装饰器的 __name__ 和 __doc___
__name__:函数名
__doc___:函数的解释
普通函数
def func1(): """ 此函数是完成登陆的功能,参数分别是...作用。 return: 返回值是登陆成功与否(True,False) """ print(666) func1() print(func1.__name__) #获取函数名 print(func1.__doc__) #获取函数名注释说明 执行输出: 666 func1 此函数是完成登陆的功能,参数分别是...作用。 return: 返回值是登陆成功与否(True,False)
这个有什么用呢?比如日志功能,需要打印出谁在什么时间,调用了什么函数,函数是干啥的,花费了多次时间,这个时候,就需要获取函数的有用信息了
带装饰器的函数
def wrapper(f): # f = func1 def inner(*args,**kwargs): #聚合, args (1,2,3) '''执行函数之前的相关操作''' ret = f(*args,**kwargs) # 打散 1,2,3 '''执行函数之后的相关操作''' return ret return inner @wrapper def func1(): """ 此函数是完成登陆的功能,参数分别是...作用。 return: 返回值是登陆成功与否(True,False) """ print(666) return True func1() print(func1.__name__) print(func1.__doc__) 执行输出: 666 inner 执行函数之前的相关操作
函数装饰之后,相当于执行了inner函数,所以输出inner
为了解决这个问题,需要 调用一个模块wraps
wraps将 被修饰的函数(wrapped) 的一些属性值赋值给修饰器函数(wrapper) ,最终让属性的显示更符合我们的直觉
from functools import wraps def wrapper(f): # f = func1 @wraps(f) #f是被装饰的函数 def inner(*args,**kwargs): #聚合args (1,2,3) '''执行函数之前的相关操作''' ret = f(*args,**kwargs) # 打散 1,2,3 '''执行函数之后的相关操作''' return ret return inner @wrapper def func1(): """ 此函数是完成登陆的功能,参数分别是...作用。 return: 返回值是登陆成功与否(True,False) """ print(666) return True func1() print(func1.__name__) print(func1.__doc__) 执行输出: 666 func1 此函数是完成登陆的功能,参数分别是...作用。 return: 返回值是登陆成功与否(True,False)
欢迎大家关注我的头条号,私信“python",学习资料包免费分享给需要的朋友,另有python学习交流群,可以交流学习拓展人脉。
相关推荐
- “版本末期”了?下周平衡补丁!国服最强5套牌!上分首选
-
明天,酒馆战棋就将迎来大更新,也聊了很多天战棋相关的内容了,趁此机会,给兄弟们穿插一篇构筑模式的卡组推荐!老规矩,我们先来看10职业胜率。目前10职业胜率排名与一周前基本类似,没有太多的变化。平衡补丁...
- VS2017 C++ 程序报错“error C2065:“M_PI”: 未声明的标识符"
-
首先,程序中头文件的选择,要选择头文件,在文件中是没有对M_PI的定义的。选择:项目——>”XXX属性"——>配置属性——>C/C++——>预处理器——>预处理器定义,...
- 东营交警实名曝光一批酒驾人员名单 88人受处罚
-
齐鲁网·闪电新闻5月24日讯酒后驾驶是对自己和他人生命安全极不负责的行为,为守护大家的平安出行路,东营交警一直将酒驾作为重点打击对象。5月23日,东营交警公布最新一批饮酒、醉酒名单。对以下驾驶人醉酒...
- Qt界面——搭配QCustomPlot(qt platform)
-
这是我第一个使用QCustomPlot控件的上位机,通过串口精确的5ms发送一次数据,再将读取的数据绘制到图表中。界面方面,尝试卡片式设计,外加QSS简单的配了个色。QCustomPlot官网:Qt...
- 大话西游2分享赢取种族坐骑手办!PK趣闻录由你书写
-
老友相聚,仗剑江湖!《大话西游2》2021全民PK季4月激燃打响,各PK玩法鏖战齐开,零门槛参与热情高涨。PK季期间,不仅各种玩法奖励丰厚,参与PK趣闻录活动,投稿自己在PK季遇到的趣事,还有机会带走...
- 测试谷歌VS Code AI 编程插件 Gemini Code Assist
-
用ClaudeSonnet3.7的天气测试编码,让谷歌VSCodeAI编程插件GeminiCodeAssist自动编程。生成的文件在浏览器中的效果如下:(附源代码)VSCode...
- 顾爷想知道第4.5期 国服便利性到底需优化啥?
-
前段时间DNF国服推出了名为“阿拉德B计划”的系列改版计划,截至目前我们已经看到了两项实装。不过关于便利性上,国服似乎还有很多路要走。自从顾爷回归DNF以来,几乎每天都在跟我抱怨关于DNF里面各种各样...
- 掌握Visual Studio项目配置【基础篇】
-
1.前言VisualStudio是Windows上最常用的C++集成开发环境之一,简称VS。VS功能十分强大,对应的,其配置系统较为复杂。不管是对于初学者还是有一定开发经验的开发者来说,捋清楚VS...
- 还嫌LED驱动设计套路深?那就来看看这篇文章吧
-
随着LED在各个领域的不同应用需求,LED驱动电路也在不断进步和发展。本文从LED的特性入手,推导出适合LED的电源驱动类型,再进一步介绍各类LED驱动设计。设计必读:LED四个关键特性特性一:非线...
- Visual Studio Community 2022(VS2022)安装图文方法
-
直接上步骤:1,首先可以下载安装一个VisualStudio安装器,叫做VisualStudioinstaller。这个安装文件很小,很快就安装完成了。2,打开VisualStudioins...
- Qt添加MSVC构建套件的方法(qt添加c++11)
-
前言有些时候,在Windows下因为某些需求需要使用MSVC编译器对程序进行编译,假设我们安装Qt的时候又只是安装了MingW构建套件,那么此时我们该如何给现有的Qt添加一个MSVC构建套件呢?本文以...
- Qt为什么站稳c++GUI的top1(qt c)
-
为什么现在QT越来越成为c++界面编程的第一选择,从事QT编程多年,在这之前做C++界面都是基于MFC。当时为什么会从MFC转到QT?主要原因是MFC开发界面想做得好看一些十分困难,引用第三方基于MF...
- qt开发IDE应该选择VS还是qt creator
-
如果一个公司选择了qt来开发自己的产品,在面临IDE的选择时会出现vs或者qtcreator,选择qt的IDE需要结合产品需求、部署平台、项目定位、程序猿本身和公司战略,因为大的软件产品需要明确IDE...
- Qt 5.14.2超详细安装教程,不会来打我
-
Qt简介Qt(官方发音[kju:t],音同cute)是一个跨平台的C++开库,主要用来开发图形用户界面(GraphicalUserInterface,GUI)程序。Qt是纯C++开...
- Cygwin配置与使用(四)——VI字体和颜色的配置
-
简介:VI的操作模式,基本上VI可以分为三种状态,分别是命令模式(commandmode)、插入模式(Insertmode)和底行模式(lastlinemode),各模式的功能区分如下:1)...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- “版本末期”了?下周平衡补丁!国服最强5套牌!上分首选
- VS2017 C++ 程序报错“error C2065:“M_PI”: 未声明的标识符"
- 东营交警实名曝光一批酒驾人员名单 88人受处罚
- Qt界面——搭配QCustomPlot(qt platform)
- 大话西游2分享赢取种族坐骑手办!PK趣闻录由你书写
- 测试谷歌VS Code AI 编程插件 Gemini Code Assist
- 顾爷想知道第4.5期 国服便利性到底需优化啥?
- 掌握Visual Studio项目配置【基础篇】
- 还嫌LED驱动设计套路深?那就来看看这篇文章吧
- Visual Studio Community 2022(VS2022)安装图文方法
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)