第九篇、装饰器
一、阅读目录
首先:你是否还在为装饰器到底是什么苦苦发愁?是不是还在知乎,百度上面苦苦查询到底是什么,到底怎么用,苍天啊,那些长篇大论到底是什么啊,卧槽卧槽、语法糖? 没关系,来我来帮你缕缕什么是装饰器,额,或许你看到这篇标题,心中正嗤之以鼻,不就是个装饰器吗?老子。。。。可是你真的知道装饰器吗?不如往下看吧

1、闭包 2、装饰器概念 3、不带参数的装饰器 4、原函数带参数的装饰器 5、带参数的装饰器 6、两个装饰器 7、装饰器的应用场景
二、闭包
1、闭包:内部函数包含对外部作用域而非全局作用域的引用
提示:之前我们都是通过参数将外部的值传给函数,闭包提供了另外一种思路,包起来喽,包起呦,包起来哇
如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def counter(): n=0 def incr(): nonlocal n x=n n+=1 return x return incr c=counter() print(c()) print(c()) print(c()) print(c.__closure__[0].cell_contents) #查看闭包的元素 |
2、闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域
应用领域:延迟计算(原来我们是传参,现在我们是包起来)
from urllib.request import urlopen
def index(url):
def get():
return urlopen(url).read()
return get
baidu=index('http://www.baidu.com')
print(baidu().decode('utf-8'))
3、闭包函数的具体用噶
a、是定义在函数内部的函数
b、包含对外部作用域名字的引用,而不是对全局作用域名字的引用
如:
1 2 3 4 5 6 7 | def a(): x=111 def b(): print(x) return b #为了打破层级关系限制,可以在任意位置调用这个函数 c=a() #为了打破层级关系限制,可以在任意位置调用这个函 c() |
上面就满足两个条件,一、在a中包含了b这个函数,二、引用了a中的局部变量
闭包小例子:

def index(url): def get(): return requests.get(url).text return get aa=index() aa("www.baidu.com")
三、装饰器概念
1、什么是装饰器?
装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
强调装饰器的原则:1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式
装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能
2、为何要用装饰器:
开放封闭原则:对修改封闭,对扩展开放
四、原函数不带参数的装饰器
先放代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import time def timer(func): def wrapper(): start=time.time() res=func() stop=time.time() print( "run time is %s" %(stop-start)) return res return wrapper @timer def index(): time.sleep(3) print( "welcome to index" ) return 123 index() |
上面执行顺序首先会从上倒下加载 timer函数,然后执行@timer这个装饰器,再把index这个函数名当成参数传递到timer函数中,然后执行wrapper函数,这里就是闭包
然后执行下面的index(),这里的index就是wapper,wapper加上括号就是执行wrapper函数的方法,然后执行start,执行到func()的时候,由于func就是index,所以指针
跳到装饰器执行下面原来index()中的方法,(注意index已经被改变了,现在没有index了),然后执行stop依次执行
五、原函数带参数的装饰器
在实际中,我们的装饰器可能应用到不同的函数中去,这些函数的参数都不一样,和函数传参是一样的,下面来看看如何将其应用到装饰器中去。
import time def timer(func): def wrapper(*args,**kwargs): start=time.time() res=func(*args,**kwargs) stop=time.time() print("run time is %s" %(stop-start)) return res return wrapper @timer def index(name): time.sleep(3) print("welcome to here %s" %name) return 123 res=index("pyrene") print(res)
看着是不是和上面的类似,额、、、其实就是加上了参数,还不懂?好吧,下面解释
首先wrapper函数 中为什么要加参数?由于index已经被改变了,变成了wapper,所以你下面要执行,传入name,所以wapper中必须要有参数啦
func函数中为什么要有参数?那是因为func就是index,下面函数中有了参数,所以func里面也要有
六、使用两个装饰器
当一个装饰器不够用的话,我们就可以用两个装饰器,当然理解起来也就更复杂了,当使用两个装饰器的话,首先将函数与内层装饰器结合然后在与外层装饰器相结合,要理解使用@语法的时候到底执行了什么,是理解装饰器的关键。这里还是用最简单的例子来进行说明。
def outer2(func2): def inner2(*args,**kwargs): print('开始') r=func2(*args,**kwargs) print('结束') return r return inner2 def outer1(func1): def inner1(*args,**kwargs): print('start') r=func1(*args,**kwargs) print('end') return r return inner1 @outer2 # 这里相当于执行了 f=outer1(f) f=outer2(f),步骤如下 @outer1 #1、f=outer1(f) f被重新赋值为outer1(1)的返回值inner1, def f(): # 此时func1为 f():print('f 函数') print('f 函数') #2、f=outer2(f) 类似f=outer2(inner1) f被重新赋值为outer2的返回值inner2 # 此时func2 为inner1函数 inner1里面func1函数为原来的 f():print('f 函数') f() # 相当于执行 outer2(inner1)() 执行结果如下: 开始 # 在outer函数里面执行,首先打印 ‘开始 ’ start # 执行func2 即执行inner1函数 打印 ‘start’ f 函数 # 在inner1函数里面执行 func1 即f()函数,打印 ‘f 函数’ end # f函数执行完,接着执行inner1函数里面的 print('end') 结束 # 最后执行inner2函数里面的 print('结束')
python执行代码的时候碰到两个装饰器解释过程:
将@outer1和f()先执行,函数outer1()将返回值inner1重新赋值给f,此时f指向的函数体是inner1的函数体,这里称f为新f
@outer2将新f当作参数传入inner2(),然后将返回值inner2重新赋值给f,此时f指向的函数体是inner2的函数体
执行过程和解释过程是相反的:从上到下的,先通过第一层@outer2 -----> @outer1 -------> f(),结合执行结果你就很明白了。
七、带参数的装饰器
啥?带参数的装饰器?可能你会有这样的疑问,没错就是有
current_user={"user":None} def auth(auth_type="file"): def deco(func): def wrapper(*args,**kwargs): if auth_type=="file": if current_user["user"]: return func(*args,**kwargs) name=input("name").strip() passwd=input("password").strip() with open("db.txt",encoding="utf-8") as f: user_idc=eval(f.read()) if name in user_idc and passwd==user_idc[name]: res=func(*args,**kwargs) current_user["user"]=name return res else: print("user or passwd error") elif auth_type=="mysql": print("mysql") elif auth_type=="ldap": print("ldap") else: print("not valid auth_type") return wrapper return deco @auth(auth_type="mysql") #@deco #index=deco(index) def index(): print("from index") index() #index=wrapper
看过上面的代码是不是有点小蒙蔽?哈哈没关系,下面帮你分析
首先加载auth函数,然后执行装饰器,这里的@auth执行之后,你会发现其实就是执行@deco.执行@deco下面的index()其实就是wrapper(),然后后面的你就懂了吧,哈哈
至此关于装饰器神秘的面纱已经揭开了,是不是不再那么神秘了,可是装饰器会用了,你知道它到底什么用吗?
八、装饰器应用
这里稍微提一点,具体的关注我看后面的文章
装饰器的强大是有目共睹的,提供了其他语言没有的便捷,其中装饰器可以用在面向对象修饰符、多线程、多进程、协程等很多方面~
好啦,装饰器解释已经完毕,如果有不懂的欢迎下方留言,或者加我的qq3110436742探讨。如果有什么错误,来拍砖啊
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步