python基础5之装饰器详解编程语言

内容概要:

一、装饰器前期知识储备

1.python解释函数代码过程:

python解释器从上往下顺序解释代码,碰到函数的定义代码块不会立即执行它,而是将其放在内存中,等到该函数被调用时,才执行其内部的代码块。

2.函数即“变量”:

函数的使用分为,函数的定义和函数的调用,调用方式是为函数名后加括号(函数名存放的是内存地址)类似于变量的使用(先定义,后使用)。可以用一个很形象的列子比喻,函数体相当于一个房间里的工具,函数名相当于门牌号(内存地址)。

python基础5之装饰器详解编程语言

示列:

python基础5之装饰器详解编程语言

 1 #/usr/bin/env python 
 2 # -*- coding:utf-8 -*- 
 3 #Author:W-D 
 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

3.变量的回收机制:

前面说过函数即变量,当变量的引用阶数为0的时候,其变量的内存才会被回收,并且我们使用del删除的时候只是删除了变量的指向地址,实际变量的值的内存是python定时回收的。tips:变量的阶就是变量被引用的次数,例如x=1,阶就是1,若此时y=x,阶数就是2.

python基础5之装饰器详解编程语言

 1 #报错代码 
 2 #/usr/bin/env python 
 3 # -*- coding:utf-8 -*- 
 4 #Author:W-D 
 5 def test(): 
 6     print("this is test") 
 7 bar()#未定义就先进行了调用,会报错 
 8 def bar(): 
 9     print("this is bar") 
10 test() 
11 ######未报错代码 
12 #/usr/bin/env python 
13 # -*- coding:utf-8 -*- 
14 #Author:W-D 
15 def test(): 
16     print("this is test") 
17     bar() 
18 def bar(): 
19     print("this is bar") 
20 test()#当解释器运行到这里时候,bar函数已经经过解释器解释过了,存在了内存中,此时test函数中再调用bar不会报错。 
21 结果: 
22 this is test 
23 this is bar

 4.函数的嵌套:

函数的嵌套就是在一个函数中定义一个或者多个函数,并且嵌套的函数只能在内层调用,函数外部不能直接调用。

示列:

python基础5之装饰器详解编程语言

 1 #/usr/bin/env python 
 2 # -*- coding:utf-8 -*- 
 3 #Author:W-D 
 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:W-D 
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#执行了内层函数代码 
24                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      

5.高阶函数

满足两个规则之一:

a.把一个函数名当作实参传递给另外一个函数;

b.返回值中包含函数名;

示例:

python基础5之装饰器详解编程语言

 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

6.装饰器

定义:本质是函数,作用给其他函数添加附加功能

原则:a.不能修改被装饰函数的源代码

        b.不能修改被装饰函数的调用方式

实现装饰器的步骤:高阶函数+嵌套函数–>装饰器

装饰器使用场景:

公司的网站以前是没有认证功能的,现在产品经理需要你在除了首页以外的地方都加上认证功能,要求不能修改源代码。

假如公司的网站网站如下:

 1 #!/usr/bin/env python 
 2 # -*- coding:utf-8 -*- 
 3 # Author:W-D 
 4 def person(): 
 5     print("个人板块") 
 6 def pub(): 
 7     print("公共板块") 
 8 def index(): 
 9     print("首页") 
10 index() 
11 pub() 
12 person(

装饰器解决方案:

 1 #!/usr/bin/env python 
 2 # -*- coding:utf-8 -*- 
 3 # Author:W-D 
 4 Username="wd" 
 5 passwd="123qwe" 
 6 def auth(fun): 
 7     def deco():#装饰函数 
 8         username=input("plese input your username>>:") 
 9         password=input("plese input your password>>:") 
10         if username==Username and password==passwd:#认证功能 
11             fun() 
12         else: 
13             exit("invaild username or passwd!") 
14     return deco#返回装饰函数的内存地址 
15  
16 @auth#添加装饰器 
17 def person(): 
18     print("个人板块") 
19 def pub(): 
20     print("公共板块") 
21  
22 def index(): 
23     print("首页") 
24 index() 
25 pub() 
26 person() 
27 结果: 
28 首页 
29 公共板块 
30 plese input your username>>:wd 
31 plese input your password>>:123qwe 
32 个人板块#输入了用户名密码以后才能看见个人模块

 

实现过程:

1.正如上面所说,装饰器的实现需要高阶函数+嵌套函数,那么我们一步一步来看怎么样实现的:

第一步:满足要求一,利用return不改变其源代码的调用方式,把封闭的内层函数地址返回出来,可以供外部调用。

python基础5之装饰器详解编程语言

 1 def deco(fun): 
 2     username=input("plese input your username>>:") 
 3     password=input("plese input your password>>:") 
 4     if username==Username and password==passwd: 
 5         fun() 
 6     else: 
 7         exit("invaild username or passwd!") 
 8     return deco#返回deco的内存地址 
 9 def person(): 
10     print("个人板块") 
11 person=deco(person)#将返回的地址赋值给person 
12 person()#调用person相当于调用deco,为修改其调用方式,但是person已经被偷梁换柱了 
13 结果: 
14 plese input your username>>:wd 
15 plese input your password>>:ada 
16 invaild username or passwd!

第二步:满足要求二,利用嵌套函数。

python基础5之装饰器详解编程语言

 1 def auth(fun): 
 2     def deco():#定义了一个内层函数,相当于“变量” 
 3         username=input("plese input your username>>:") 
 4         password=input("plese input your password>>:") 
 5         if username==Username and password==passwd: 
 6             fun() 
 7         else: 
 8             exit("invaild username or passwd!") 
 9     return deco#返回deco内存地址 
10 def person(): 
11     print("个人板块") 
12 person=auth(person)#将返回的deco内存地址复制给person 
13 person()#此时调用person相当于调用deco,未改变调用方式,但是此时的person已经不是原来的person。 
14 结果: 
15 plese input your username>>:wd 
16 plese input your password>>:123qwe 
17 个人板块

 第三步:关于语法糖@

python中有很多优化语法,例如在我前面代码中使用的@,相当与在这里执行了person=auth(person),此时相当于调用了已经调用了auth函数,将deco函数对象返回给person。

这个语法是死的,这里的语法为:被装饰的函数的名字会被当作参数传递给装饰函数。装饰函数执行它自己内部的代码后,会将它的返回值赋值给被装饰的函数。

python基础5之装饰器详解编程语言

 1 def auth(fun): 
 2     print("in the auth") 
 3     def deco(): 
 4         username=input("plese input your username>>:") 
 5         password=input("plese input your password>>:") 
 6         if username==Username and password==passwd: 
 7             fun() 
 8         else: 
 9             exit("invaild username or passwd!") 
10     return deco 
11 @auth#相当与执行person=auth(person)这里已经调用了auth函数,目的将deco函数偷梁换柱成person 
12 def person(): 
13     print("个人板块") 
14 person() 
15 结果: 
16 in the auth#该结果在调用person前面就会出现,验证时候注释掉person()即可验证 
17 plese input your username>>:wd 
18 plese input your password>>:123qwe 
19 个人板块

 

二、装饰器

前面已经实现了装饰器,但是被装饰的函数一般来说都有自己的参数,所以来看下一般的装饰器:

 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>>:wd 
16 plese input your password>>:123qwe 
17 wd的个人板块

装饰器内部原理:

内部原理我们可以从上面的代码解释说明:

1.python解释器从上往下解释运行代码,到读到def auth(fun)的时候,解释器发现它是一个函数,于是将函数体加载在内存中,[email protected]

[email protected],此时相当于解释到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

 

三、装饰器高潮版

当被装饰的函数有返回值时,这时候我们该怎么处理呢,请看下面的表演。

先观察下面例子,如果按照原来的方法,代码执行完,被修饰函数的返回值丢了;

def logger(fun): 
    def deco(): 
        print("添加日志功能") 
        fun() 
    return deco 
@logger 
def person(): 
    print("欢迎来到个人专区") 
    return "hello" 
a=person() 
print(a) 
结果: 
添加日志功能 
欢迎来到个人专区 
None 
#可以看到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

 

四、装饰器之最终高潮版–带参数的装饰器

有人可能会问,既然装饰器本质上也是函数,那它可以传递参数吗?当然可以,请继续看下面的表演;

username="wd" 
passwd="123qwe" 
def logger(type):#装饰器传递的参数在这里 
    def auth(fun):#这里传递被装饰的函数作为参数 
            def deco(*args,**kwargs):#原来的装饰函数 
                print("添加日志功能") 
                if type=="local": 
                    _username=input("please input your username>>") 
                    _passwd = input("please input your username>>") 
                    if _username==username and _passwd==passwd: 
                        print("本地认证成功!") 
                        res = fun(*args,**kwargs) 
                        return res 
                        #在这里返回res,其他地方可能res这个变量不存在导致报错 
                    else: 
                        print("认证失败!") 
                elif type=="remote": 
                    print("远程认证成功!") 
                else: 
                    print("认证失败,再见!") 
            return deco 
    return auth 
@logger(type="local") 
def person(): 
    print("欢迎来到个人专区") 
    return "hello" 
a=person() 
print(a) 
结果: 
添加日志功能 
please input your username>>wd 
please input your username>>123qwe 
本地认证成功! 
欢迎来到个人专区 
hello

 解释说明:装饰器带参数,原理是在原来的装饰函数基础之上又封装了一层函数,这层函数作用是为了接收参数(当然,参数可以是函数),用于后面的处理。

 

 

 

 

 

 

 

 

 

 

        

 

                                                                                                                                                                                                                                                                    

 

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/12464.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论