攻克python3-装饰器
装饰器
1.为什么要使用装饰器?
在产品升级中,有成千上百个函数,要为这些函数增加某些功能,如果一个个函数的修改,岂不是浪费资源,浪费时间,这是装饰器的好处就展现了,它可以在短时间内完成你对许多函数的修改。
2.什么是装饰器?
装饰器也是函数,是装饰其他函数的函数(为其他函数增加功能)
3.装饰器的原则:
a.不能修改原函数的代码
b.不改变原函数的调用方式
4.装饰器的知识储备:
a.函数即“变量”
b.高阶函数
c.嵌套函数
a.函数即“变量”
在定义函数时,系统将函数体内的函数过程作为字符串保存在内存中,当作一个变量储存,只是把地址赋给了函数名,在调用函数时,再去解释函数过程
b.高阶函数
1.一个函数的函数名作为另一个函数的参数
import time def bar(): time.sleep(2) print("in the bar") def test1(func): start_time=time.time() func() stop_time=time.time() print("the test run time is %s"%(stop_time-start_time)) bar() print("-----------") test1(bar)
通过上面的演示是不是得出一个结论:高阶函数可以在不改变原函数的源代码的情况下,为其增加功能,但会改变原函数的调用方式
2.一个函数的返回值包括函数名
import time def bar(): time.sleep(2) print("in the bar") def test1(func): start_time=time.time() func() stop_time=time.time() print("the test run time is %s" % (stop_time - start_time)) return func bar=test1(bar) print("------") bar()
通过上面的演示是不是得出一个结论:高阶函数可以不改变原函数的调用方式,但是没有添加新功能
c.嵌套函数
def foo(): print("in the foo") def bar(): print("in the bar") bar() foo()
5.写装饰器
a.装饰器是为给其他函数增加功能,但是不能改变原函数的源代码,那我们怎么才能不改变原函数的源代码,回顾上文,通过高阶函数(将函数名作为另一个函数的参数)定义一个新函数,在新函数中调用原函数,并且添加新功能,这样就可以实现我们的目标
#现在我有一个函数tets1,然后为它添加一个新功能 def dec(func): print("我是新功能!") func() def test1(): print("in the test1") test1() print("-------") dec(test1)
b.在不修改原函数的同时,而且也不能修改函数的调用方式,函数的调用方式是不是函数名(),那么函数即“变量”,在这里就起大作用了,在上面代码中,我们可以将新函数的函数地址赋给原函数的函数名,原函数的函数名对应的内存空间发生了改变,这片新的内存空间中既有原函数的功能,也有新增加的功能,那么我们通过高阶函数(返回值中包含函数名)将原函数的函数名重新赋值,这样我们的目的就达到了。
#因为需要返回新函数的函数名(函数地址),所以要使用嵌套函数 def foo(func): #func=test1 将test1函数地址传给了func 所以新函数的内存空间保存了原函数的内存地址,可以通过调用新函数来调用原函数,并且增加功能 def dec(): print("我是新功能!") func() return dec @foo #相当于test1=foo(test1) 在python中 装饰器 用@装饰圈名 来表示 def test1(): print("in the test1") test1() print("-------") #test1=foo(test1) test1()
6.高级版装饰器
import time
def timer(func): #func=test1 将test1的地址赋给func def deco(*args,**kwargs): start_time = time.time() res=func(*args,**kwargs) #相当于调用test1 stop_time=time.time() print("the func run time is %s" % (stop_time - start_time))
return res return deco #将deco的地址返回 @timer #相当于test1=timer(test1) def test1(): time.sleep(2) print("in the test1") @timer def test2(name): time.sleep(2) print("in the test2",name) return name # test1=timer(test1) #test1=deco 将deco的地址赋给test1 test1() #相当于调用deco test2("phk")
以上代码中,装饰器的功能是为两个函数增加计时功能,与之前有所不同的是,原函数中是带参数和返回值,那么我们可以使用*args **kwargs来充当函数参数,与参数一样,我们给新函数定义返回值,这样就做到了透明化处理.
7.高潮版
user,passwd="phk","123" def auth(auth_type): #print(auth_type) def outer(func): #print(func) def deco(*args,**kwargs): if auth_type == "local": username=input("usernane:").strip() password=input("password:").strip() if user==username and passwd==password: func() else: exit("账号密码错误") elif auth_type=="ldap": print("不会") return deco return outer def index(): print("welcome to index page") @auth(auth_type="local") #相当于home=auth(auyh_type="local")(func) def home(): print("welcome to home page") @auth(auth_type="ldap") def bbs(): print("welcome to bbs page") index() home() bbs()
装饰器的功能是为三个页面函数添加登陆功能,不同的是需要选择本地登陆还是ldap登陆,说白了就是在装饰器外面在套一层函数,传进来的参数可以作为局部参数,可以咱函数体内使用,但是需要明白一点
@auth(auth_type="local") 相当于home=auth(auyh_type="local")(func)。
解释器会先翻译“auth(auth_type = 'local')”,再将其返回值(假设给了_func这个不存在的函数名)当作函数指针,这里的_func函数名代表的是outer,然后再去执行“func = _func(func)”,而这个func函数名代表的其实就是deco。