内容概要:
一、装饰器前期知识储备1.python解释函数代码过程:
python解释器从上往下顺序解释代码,碰到函数的定义代码块不会立即执行它,而是将其放在内存中,等到该函数被调用时,才执行其内部的代码块。
2.函数即“变量”:
函数的使用分为,函数的定义和函数的调用,调用方式是为函数名后加括号(函数名存放的是内存地址)类似于变量的使用(先定义,后使用)。可以用一个很形象的列子比喻,函数体相当于一个房间里的工具,函数名相当于门牌号(内存地址)。
没有门牌号,系统会定期回收内存地址。
示例:
1 #/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author:SU 4 def test(): 5 print("this is test") 6 acl=test#变量赋值,内存地址赋值 7 print(acl,test)#打印函数的内存地址 8 acl()#调用,相当于调用test() 9 结果: 10 <function test at 0x0000000000B0E048> <function test at 0x0000000000B0E048> 11 this is test View Code3.变量的回收机制:
前面说过函数即变量,当变量的引用阶数为0的时候,其变量的内存才会被回收,并且我们使用del删除的时候只是删除了变量的指向地址,实际变量的值的内存是python定时回收的。tips:变量的阶就是变量被引用的次数,例如x=1,阶就是1,若此时y=x,阶数就是2.
#报错代码 #/usr/bin/env python # -*- coding:utf-8 -*- #Author:Susu def test(): print("this is test") bar()#未定义就先进行了调用,会报错 def bar(): print("this is bar") test() ######未报错代码 #/usr/bin/env python # -*- coding:utf-8 -*- #Author:Susu def test(): print("this is test") bar() def bar(): print("this is bar") test()#当解释器运行到这里时候,bar函数已经经过解释器解释过了,存在了内存中,此时test函数中再调用bar不会报错。 结果: this is test this is bar View Code4.函数的嵌套:
函数的嵌套就是在一个函数中定义一个或者多个函数,并且嵌套的函数只能在内层调用,函数外部不能直接调用。
示列:
1 #/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author:Susu 4 def outer(): 5 print("this is outer") 6 def inner(): 7 print("this is inner") 8 outer() 9 结果: 10 this is outer#结果表明只执行了外层函数的代码,因为内层没有调用 11 12 #/usr/bin/env python 13 # -*- coding:utf-8 -*- 14 #Author:Susu 15 def outer(): 16 print("this is outer") 17 def inner(): 18 print("this is inner") 19 inner()#内部调用 20 outer() 21 结果: 22 this is outer 23 this is inner#执行了内层函数代码 View Code5.高阶函数
满足两个规则之一:
a.把一个函数名当作实参传递给另外一个函数;
b.返回值中包含函数名;
示例:
1 #规则1 2 #!/usr/bin/env python 3 # -*- coding:utf-8 -*- 4 # Author:W-D 5 def test(fun):#传递函数名 6 fun() 7 print("in the test") 8 def bar(): 9 print("in the bar") 10 test(bar) 11 结果: 12 in the bar 13 in the test 14 15 规则2: 16 def test(fun): 17 fun() 18 print("in the test") 19 return fun#返回fun函数的内存地址 20 def bar(): 21 print("in the bar") 22 a=test(bar) 23 print(a) 24 a()#调用返回的函数 25 结果: 26 in the bar 27 in the test 28 <function bar at 0x000000000104F1E0> 29 in the bar View Code6.装饰器
定义:本质是函数,作用给其他函数添加附加功能
原则:a.不能修改被装饰函数的源代码
b.不能修改被装饰函数的调用方式
实现装饰器的步骤:高阶函数+嵌套函数-->装饰器
装饰器使用场景:
公司的网站以前是没有认证功能的,现在产品经理需要你在除了首页以外的地方都加上认证功能,要求不能修改源代码。
假如公司的网站网站如下:
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author:Susu def person(): print("个人板块") def pub(): print("公共板块") def index(): print("首页") index() pub() person() View Code装饰器解决方案:
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author:Susu Username="susu" passwd="123456" def auth(fun): def deco():#装饰函数 username=input("plese input your username>>:") password=input("plese input your password>>:") if username==Username and password==passwd:#认证功能 fun() else: exit("invaild username or passwd!") return deco#返回装饰函数的内存地址 @auth#添加装饰器 def person(): print("个人板块") def pub(): print("公共板块") def index(): print("首页") index() pub() person() 结果: 首页 公共板块 plese input your username>>:susu plese input your password>>:123456 个人板块#输入了用户名密码以后才能看见个人模块 View Code实现过程:
1.正如上面所说,装饰器的实现需要高阶函数+嵌套函数,那么我们一步一步来看怎么样实现的:
第一步:满足要求一,利用return不改变其源代码的调用方式,把封闭的内层函数地址返回出来,可以供外部调用。
def deco(fun): username=input("plese input your username>>:") password=input("plese input your password>>:") if username==Username and password==passwd: fun() else: exit("invaild username or passwd!") return deco#返回deco的内存地址 def person(): print("个人板块") person=deco(person)#将返回的地址赋值给person person()#调用person相当于调用deco,为修改其调用方式,但是person已经被偷梁换柱了 结果: plese input your username>>:susu plese input your password>>:123456 invaild username or passwd! View Code第二步:满足要求二,利用嵌套函数。
def auth(fun): def deco():#定义了一个内层函数,相当于“变量” username=input("plese input your username>>:") password=input("plese input your password>>:") if username==Username and password==passwd: fun() else: exit("invaild username or passwd!") return deco#返回deco内存地址 def person(): print("个人板块") person=auth(person)#将返回的deco内存地址复制给person person()#此时调用person相当于调用deco,未改变调用方式,但是此时的person已经不是原来的person。 结果: plese input your username>>:susu plese input your password>>:123456 个人板块 View Code 二、装饰器装饰的函数一般来说都有自己的参数,所以来看下一般的装饰器如何添加参数:
1 def auth(fun): 2 def deco(*args,**kwargs):#传递参数 3 username=input("plese input your username>>:") 4 password=input("plese input your password>>:") 5 if username==Username and password==passwd: 6 fun(*args,**kwargs) 7 else: 8 exit("invaild username or passwd!") 9 return deco 10 @auth 11 def person(name): 12 print("%s的个人板块"%name) 13 person("wd") 14 结果: 15 plese input your username>>:susu 16 plese input your password>>:123 17 susu的个人板块 View Code装饰器内部原理:
内部原理我们可以从上面的代码解释说明:
1.python解释器从上往下解释运行代码,到读到def auth(fun)的时候,解释器发现它是一个函数,于是将函数体加载在内存中,然后跳过到达@auth
2.解释器读取@auth发现是个装饰函数,此时相当于解释到person=auth(person),此时已经调用函数auth,并且执行函数体,发现def deco(*args,**kwargs)又是一个函数,于是将函数体加载到内存,接着跳到return deco,此时将deco函数的内存地址赋值给你person,经过这个步骤,person变量的内存地址指向了deco,下面调用person相当于调用deco。
3.解释器解释到person(),此时的person的内存地址是指向deco的,调用person就是调用deco,然后执行deco的函数体,执行deco的函数体就加了认证功能,并且在执行此时person,这样就完美的把需求实现了。
有些人可能会问,为什么需要在外部包装一层函数?
答案是:如果不再外部包装一层装饰函数,当我们使用装饰器时直接就执行了该函数,此时我们都还没有调用,然后程序已经执行了,这显然不是我们所要的。
例子:
1 def logger(fun): 2 print("日志功能") 3 fun() 4 return logger 5 @logger 6 def test(): 7 print("in the test") 8 结果:#还未调用test,logger函数就执行了 9 日志功能 10 in the test 三、装饰器高潮版当被装饰的函数有返回值时,这时候我们该怎么处理呢,请看下面的表演。
先观察下面例子,如果按照原来的方法,代码执行完,被修饰函数的返回值丢了;
1 def logger(fun): 2 def deco(): 3 print("添加日志功能") 4 fun() 5 return deco 6 @logger 7 def person(): 8 print("欢迎来到个人专区") 9 return "hello" 10 a=person() 11 print(a) 12 结果: 13 添加日志功能 14 欢迎来到个人专区 15 None 16 #可以看到person的返回值应该是hello,但是这里变成了None,这是为什么呢?因为这里person相当执行deco,此时得到的返回值是deco的返回值, 而deco函数没有返回值,所以a的值为None那么怎么样才能接收到这个返回值呢,很容易,使用变量接收fun的返回值,并将其在deco函数中return出来,请看下面代码;
def logger(fun): def deco(): print("添加日志功能") res=fun()#接收被装饰函数的返回值 return res#将结果返回出来 return deco @logger def person(): print("欢迎来到个人专区") return "hello" a=person() print(a) 结果: 添加日志功能 欢迎来到个人专区 hello View Code 四、装饰器之最终高潮版--带参数的装饰器有人可能会问,既然装饰器本质上也是函数,那它可以传递参数吗?当然可以,请继续看下面的表演;
1 username="susu" 2 passwd="123" 3 def logger(type):#装饰器传递的参数在这里 4 def auth(fun):#这里传递被装饰的函数作为参数 5 def deco(*args,**kwargs):#原来的装饰函数 6 print("添加日志功能") 7 if type=="local": 8 _username=input("please input your username>>") 9 _passwd = input("please input your username>>") 10 if _username==username and _passwd==passwd: 11 print("本地认证成功!") 12 # res = fun(*args,**kwargs) 13 # return res 14 #在这里返回res,其他地方可能res这个变量不存在导致报错 15 else: 16 print("认证失败!") 17 elif type=="remote": 18 print("远程认证成功!") 19 else: 20 print("认证失败,再见!") 21 return deco 22 return auth 23 @logger(type="local") 24 def person(): 25 print("欢迎来到个人专区") 26 return "hello" 27 person("remote") 28 a=person() 29 print(a) 30 结果: 31 添加日志功能 32 please input your username>>susu 33 please input your username>>123 34 本地认证成功! 35 欢迎来到个人专区 36 hello解释说明:装饰器带参数,原理是在原来的装饰函数基础之上又封装了一层函数,这层函数作用是为了接收参数(当然,参数可以是函数),用于后面的处理。
还可以参考ALEX博客
http://www.cnblogs.com/alex3714/articles/5765046.html
转载于:https://www.cnblogs.com/xiangjun555/articles/6837129.html