Python装饰器
1|0一、知识前提(函数进阶部分)
了解装饰器的三个前提。
1|11.1 作用域
参考Python 函数 第五部分 :点这里
1|21.2 函数即对象(函数的本质)
在python的世界里,函数和我们之前的[1,2,3],'abc',8等一样都是对象,而且函数是最高级的对象(对象是类的实例化,可以调用相应的方法,函数是包含变量对象的对象)。
函数名本质上就是函数的内存地址:
函数对象的调用仅仅比其它对象多了一个()而已!foo,bar与a,b一样都是个变量名。
那上面的问题也就解决了,只有函数加载到内存才可以被调用。
既然函数是对象,那么自然满足下面两个条件:
1.2.1 其可以被赋给其他变量
1.2.2 其可以被定义在另外一个函数内(作为参数&作为返回值),类似于整形,字符串等对象。
注意:这里说的函数都是指函数名,比如foo;而foo()已经执行函数了,foo()是什么类型取决于return的内容是什么类型!!!
另外,如果大家理解不了对象,那么就将函数理解成变量,因为函数对象总会由一个或多个变量引用,比如foo,bar。
1|31.3 函数的嵌套以及闭包(closure)
1.3.1 函数的嵌套
是的,bar就是一个变量名,有自己的作用域的。
Python允许创建嵌套函数。通过在函数内部def的关键字再声明一个函数即为嵌套:
正常情况下,函数有自己的作用域
既然这样,i()执行的时候outer函数已经执行完了,为什么inner还可以调用outer里的变量x呢?
因为:outer里return的inner是一个闭包函数,有x这个环境变量。
1.3.2 闭包
定义:如果在一个内部函数里,对在外部作用域(encloing)(但不是在全局作用域(global))的变量进行引用,那么内部函数就被认为是闭包(closure).
如上实例,inner就是内部函数,inner里引用了外部作用域的变量x(x在外部作用域outer里面,不是全局作用域),
则这个内部函数inner就是一个闭包。
再稍微讲究一点的解释是,闭包=函数块+定义函数时的环境,inner就是函数块,x就是环境,当然这个环境可以有很多,不止一个简单的x。

-
a = [1,2]
def fun():
# global a
# a = [2,3]
a[0] = 2
a[1] = 4
fun()
print(a) # [2,3]或者[2,4]
-

闭包函数判断(__closure__)
2|0二 装饰器概念
要解决的三件事:1、不同函数雷同功能。2、开闭原则。3、重复利用
装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
业务生产中大量调用的函数:
现在有一个新的需求,希望可以记录下函数的执行时间,于是在代码中添加日志代码:
以上代码虽然实现了功能,但是需要更改代码,这直接违反了软件开发中的一个原则“开放-封闭”(开闭)原则,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
- 封闭:已实现的功能代码块不应该被修改
- 开放:对现有功能的扩展开放
而且bar()、bar2()也有类似的需求,怎么做?再在bar函数里调用时间函数?这样不仅违反开闭原则,就造成大量雷同的代码,为了减少重复写代码,我们可以这样做,重新定义一个高阶函数,就是把一个函数当做一个参数传给另外一个函数:专门设定时间。
逻辑上不难理解,而且运行正常。 但是这样的话,你基础平台的函数修改了名字,容易被业务线的人投诉的,因为我们每次都要将一个函数作为参数传递给show_time函数。而且这种方式已经破坏了原有的代码逻辑结构,之前执行业务逻辑时,执行运行foo(),但是现在不得不改成show_time(foo)。那么有没有更好的方式的呢?当然有,答案就是装饰器。
3|0三 装饰器思路
首先,以下两种写法是一样的
给函数赋值变量名就像def func_name 是一样的效果,如下面的plus(n)函数,你调用时可以用plus名,还可以再起个其它名字,如
那么,
之所改变了调用方式,是因为用户每次调用时需要执行login(henan),类似的。其实稍一改就可以了
这样当调用 foo 时,其实相当于调用了show_time(foo), 这样,功能就实现了。但是,问题在于,还不等调用 ,你的 foo = show_time(foo)就会先自己把foo执行了呀。(在show_time(foo)这一步执行,直接在show_time中传入foo执行了)。应该等调用foo的时候再执行。
最终,使用闭包,写一个简单装饰器。
函数show_time就是装饰器,它把真正的业务方法func包裹在函数里面,看起来像foo被上下时间函数装饰了。在这个例子中,函数进入和退出时 ,被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。
@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作。
如上所示,这样我们就可以省去 foo = show_time(foo) 这一句了,直接调用 foo() 即可得到想要的结果。如果我们有其他的类似函数,我们可以继续调用装饰器来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。
这里需要注意的问题: foo=show_time(foo) 其实是把 wrapper 引用的对象引用给了 foo,而 wrapper 里的变量 func 之所以可以用,就是因为wrapper是一个闭包函数。
@show_time帮我们做的事情就是当我们执行业务逻辑foo()时,执行的代码由粉框部分转到蓝框部分,仅此而已!
装饰器在Python使用如此方便都要归因于Python的函数能像普通的对象一样能作为参数传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内。
4|0四 带参数的被装饰函数
下面的是返回原函数的return值,如果没有手动哦return,return一直为空
4|14.1 不定长参数
为什么要不定长,因为一个装饰器会被多个函数使用,而每个函数的参数形式都不一样,所以必须得写成不定长形式
4|24.2 固定模式
5|0五 带参数的装饰器
假如你有成千上万个函数使用了一个装饰器,现在你想把这些装饰器都取消掉,你要怎么做?
一个一个的取消掉? 没日没夜忙活3天。。。
过两天你领导想通了,再让你加上。。。
@timmer_out(FLAG) timmer = timmer_out(FLAG)
第一步,timmer_out(FLAG),返回timmer
第二步,@timmer,等于@timmer等于 wahaha=timmer(wahaha)
上面的timmer_out是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器(一个含有参数的闭包函数)。当我 们使用@timmer_out(FLAG)调用的时候,Python能够发现这一层的封装,并把参数传递到装饰器的环境中。
6|0六 多层装饰器
过程:
7|0七 类装饰器
再来看看类装饰器,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器还可以依靠类内部的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
8|0八 functools.wraps
使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、__name__、参数列表,先看例子:
等价于:
不难发现,函数f被wrapper取代了,当然它的docstring,__name__就是变成了wrapper函数的信息了。
这个问题就比较严重的,好在我们有functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。
9|0九 内置装饰器
@staticmathod
@classmethod
@property
学习类的时候我们详细介绍的...
9|1十 补充
__EOF__

本文链接:https://www.cnblogs.com/dongye95/p/10085817.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!