• python基础5之装饰器


    内容概要:

    一、装饰器前期知识储备

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

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

    2.函数即“变量”:

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

    示列:

     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
    View Code

    3.变量的回收机制:

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

     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
    View Code

     4.函数的嵌套:

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

    示列:

     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                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
    View Code

    5.高阶函数

    满足两个规则之一:

    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 Code

    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不改变其源代码的调用方式,把封闭的内层函数地址返回出来,可以供外部调用。

     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!
    View Code

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

     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 个人板块
    View Code

     第三步:关于语法糖@

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

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

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

    装饰器内部原理:

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

    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
    三、装饰器高潮版

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

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

    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

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

            

                                                                                                                                                                                                                                                                        

  • 相关阅读:
    自己的第一个网页
    第一次爬虫测试
    科学计算与可视化
    python自顶向下的设计方法进行体育竞技分析
    python PIL库的相关操作
    python 关于身份证号码的相关操作
    jieba库
    汉诺塔
    Django项目中运行Scrapy项目
    **Django+Echart实现多个饼状图(echart数据格式问题 {value: 135, name: '视频广告'})
  • 原文地址:https://www.cnblogs.com/wdliu/p/6380439.html
Copyright © 2020-2023  润新知