zoukankan      html  css  js  c++  java
  • day04-装饰器

    一、装饰器定义

    1)装饰器:本质是函数。

    2)功能:用来装饰其他函数,顾名思义就是,为其他的函数添加附件功能的。

    二、原则

    1)不能修改被装饰函数的源代码

    2)不能修改被装饰函数的调用方式

    如果你写的这个程序在生产环境下已经运行了,如果修改别人的源代码或者修改别人的调用方式,那么出了问题,后果可想而知

    三、实现装饰器知识储备

    1)函数即"变量"

    2)高阶函数

    3)嵌套函数

    最终: 高阶函数+嵌套函数 => 装饰器

    3.1 函数即变量

    1)python的内存机制

    #变量

    x = 1

    #函数

    def test():

    pass

    以上一个变量一个函数在内存中的表现形式如下图:

    在python解释器中,有一个概念叫做引用基数,比方说,x=1,它会先在内存当中把1这个值实实在在的存放下来,这个x其实就是1的门牌号,也是对1的一次引用。python什么时候把这个1这个屋子清空呐?它会等到1所对应的门牌号都没有了,就会把1这里面的东西给清掉,这个也是python的内存回收机制,就是靠这种方式回收的。

    2)del清理

    那我们用什么清理呐?用del去清理门牌号,就是对1的值引用的变量,del  x就表示清理掉1对应的x的门派号。如果x没有被del,则x永远不还被删除,除非程序结束了,不然永远不会被删除。del删除的不是1,只是把门牌号x删除了,只是定期刷新时,发现1没有被其他门牌号引用了,才会被清掉。

    3)函数在内存的表现形式

    ①bar函数在foo函数之后定义

    #bar函数在foo函数之后定义
    def foo():
        print("in the foo")
        bar()
    
    def bar():
        print("in the bar")
    
    foo()
    

    #输出

    in the foo

    in the bar

    bar函数是在foo函数之前定义

    # bar函数是在foo函数之前定义
    def bar():
        print("in the bar")
    
    def foo():
        print("in the foo")
        bar()
    
    foo()
    

    #输出

    in the foo

    in the bar

    显然,两种写法效果是一样的,那我们来看看第三种情况。

    ③bar函数在foo函数调用之后声明

    # bar函数在foo函数调用之后声明
    def foo():
        print("in the foo")
        bar()
    
    foo()
    
    def bar():
        print("in the bar")
    

    #输出

    Traceback (most recent call last):

    in the foo

      File "D:/PycharmProjects/pyhomework/day4/装饰器/函数即变量.py", line 31, in <module>

        foo()

      File "D:/PycharmProjects/pyhomework/day4/装饰器/函数即变量.py", line 29, in foo

        bar()

    NameError: name 'bar' is not defined  #bar函数没有定义

     

    3.2 高阶函数

    实现高阶函数有两个条件:

    1)把一个函数名当做实参传给另外一个函数

    2)返回值中包含函数名

    1、把一个函数名当做实参传给另外一个函数

    作用:在不修改被装饰函数源代码的情况下为其添加功能

    import  time
    def bar():
        time.sleep(3)
        print("in the bar")
    
    def test1(func):
        print(func)         #相当于print(bar) 函数的内存地址
        start_time = time.time()
        func()              #相当于bar() 进入函数内部执行
        stop_time = time.time()
        print("the func run the is %s"%(stop_time-start_time))
    #没有修改bar的代码
    test1(bar)  #把bar函数名当做实参传到test1中
    

      

    #输出

    <function bar at 0x0000000000A7D378>  #bar函数的内存地址

    in the bar                               #函数值

    the func run the is 2.9912972450256348

    2、返回值中包括函数名

    作用:不修改函数调用方式

    import  time
    
    def bar():
        time.sleep(3)
        print("in the bar")
    
    def test2(func):
        print(func)
        return func   #返回函数的内存地址
    
    #调用test2函数
    bar = test2(bar)   #重新给bar赋值,打印内存地址(内存地址加上小括号就能打印函数值)
    bar()  #bar函数调用方式不变,实现装饰器的功能
    

     

    相当于@bar

    #输出

    <function bar at 0x0000000000B6D378>  #打印bar函数的内存地址

    in the bar

    3.3 嵌套函数

    1、定义

    在一个函数的函数体内,用def 去声明一个函数,而不是去调用其他函数,称为嵌套函数。

    嵌套函数例子:

    def foo():
        print("in the foo")
        def bar():  #在foo函数体内,用def声明一个函数
            print("in the bar")
        bar()
    #调用foo函数
    foo()
    

     

    #输出

    in the foo

    in the bar

    下面这种情况是不是嵌套函数? 

    def bar():
        print("in the bar")
    
    def foo():
        print("in the foo")
        bar()  #调用bar函数
    
    foo()
    

    很显然不是,因为只是调用了bar函数,没有用def去声明一个函数。

      

    局部作用域和全局作用域的访问顺序

    #局部作用域和全局作用域的访问顺序
    x=0
    def grandpa():
        x=1
        def dad():
            x=2
            def son():
                x=3
                print(x)
            son()
        dad()
    #调用grandpa
    grandpa()
    

    很显然最后输出的是3,这个说明作用域:只能是从里往外找,一层一层的的找。

    四、装饰器实现

    4.1 定义

    装饰器实现的条件:高阶函数+嵌套函数 =》装饰器

    import time
    
    #定义内置函数
    def timmer(func):  #timmer(test1) func=test1
        def deco():
            start_time = time.time()
            func()   #run test1()
            stop_time = time.time()
            print("the func run time is %s"%(stop_time-start_time))
        return deco           #返回deco的内存地址
    
    #装饰test1函数
    @timmer  
    # 相当于test1 = timmer(test1)      test1(),调用deco的内存值。同时也有定义一个test1的变量
    def test1():
        time.sleep(3)
        print("in the test1")
    
    #直接执行test1函数
    test1()
    

    #输出

    in the test1

    the func run time is 3.0002999305725098

    执行步骤:

    1. 执行timmer函数,timmer(test1) 返回值赋值给test1变量,即test1=timmer(test1)
    2. 此时的test1的值是执行timmer函数返回值deco,即test1=deco
    3. 所以执行test1,其实就是执行的是deco函数,test1()其实就是执行deco函数。

    4.2 执行函数带参数

    import time
    
    def timmer(func):  #timmer(test2) func=test2
        def deco():
            start_time = time.time()
            func()   #run test2()
            stop_time = time.time()
            print("the func run time is %s"%(stop_time-start_time))
    
        return deco
    
    @timmer
    def test2(name,age):
        print("name:%s,age:%s"%(name,age))
    
    test2()
    

      

    #输出

    Traceback (most recent call last):

      File "D:/PycharmProjects/pyhomework/day4/装饰器/装饰器高潮.py", line 23, in <module>

        test2()

      File "D:/PycharmProjects/pyhomework/day4/装饰器/装饰器高潮.py", line 8, in deco

        func()   #run test1()

    TypeError: test2() missing 2 required positional arguments: 'name' and 'age' #缺少传入name和age参数

     

    很显然是错误的。因为这边执行的test2函数其实就是执行的deco函数,deco函数体内的func()其实就是执行test2函数,但是,test2需要传入name和age两个参数,所以报错。那怎么解决呢?

    传入确定参数:

    import time
    
    def timmer(func):  #timmer(test1) func=test1
        def deco(name,age):
            start_time = time.time()
            func(name,age)   #run test2()
            stop_time = time.time()
            print("the func run time is %s"%(stop_time-start_time))
    
        return deco
    
    @timmer
    def test2(name,age):
        print("name:%s,age:%s"%(name,age))
    
    test2('zhou',22)
    

      

    不能确定传入几个参数,所以我们用非固定参数传参。代码如下:

    import time
    
    def timmer(func):  #timmer(test1) func=test1
        def deco(*args,**kwargs):  #传入非固定参数
            start_time = time.time()
            func(*args,**kwargs)   #传入非固定参数
            stop_time = time.time()
            print("the func run time is %s"%(stop_time-start_time))
    
        return deco
    
    #不带参数
    @timmer  # 相当于test1 = timmer(test1)
    def test1():
        time.sleep(3)
        print("in the test1")
    
    #带参数
    @timmer
    def test2(name,age):
        print("name:%s,age:%s"%(name,age))
    #调用
    test1()
    test2("Alex",22)
    

      

    #输出

    #test1

    in the test1

    the func run time is 3.0010883808135986

    #test2

    name:Alex,age:22

    the func run time is 0.0  #test2

     

    4.3 执行函数有返回值

    def timmer(func):  #timmer(test1) func=test1
        def deco(*args,**kwargs):
            res = func(*args,**kwargs) #这边传入函数结果赋给res
            return res   # 返回res
        return deco
    
    @timmer
    def test1():  # test1 =  timmer(test1)
        print("in the test1")
        return "from the test1" #执行函数test1有返回值
    
    res = test1()
    print(res)
    

      

    #输出

    in the test1

    from the test1

     

     通过上面的例子,可以看出,其实就是在内置函数中把传入参数的执行结果赋给res,然后再返回res变量。

     

    4.4带参数装饰器

    之前我们的装饰器都是没有带参数的,其实我们已经能解决90%的问题了,但是如果说有一种情况:就是在你访问不通页面时,你用的验证的方式来源不同,这时你该怎么办?

     

    #本地验证
    user,passwd = "zhouqiongjie","abc123"
    
    def auth(auth_type):  #传递装饰器的参数
        print("auth func:",auth_type)
        def outer_wrapper(func):   # 将被装饰的函数作为参数传递进来
            def wrapper(*args,**kwargs):  #将被装饰函数的参数传递进来
                print("wrapper func args:",*args,**kwargs)
                username = input("Username:").strip()
                password = input("Password:").strip()
                if auth_type == "local":
                    if user == username and passwd == password:
                        print("33[32mUser has passed authentication33[0m")
                        res = func(*args,**kwargs)
                        print("--after authentication")
                        return res
                    else:
                        exit("Invalid username or password")
                elif auth_type == "ldap":
                    pass
            return wrapper
        return outer_wrapper
    
    def index():
        print("welcome to index page")
    
    @auth(auth_type="local")  #带参数装饰器
    def home():
        print("welcome to home page")
        return "from home"
    
    @auth(auth_type="ldap")   #带参数装饰器
    def bbs():
        print("welcome  to bbs page")
    
    index()
    print(home())
    bbs()
    

      

    上面的例子可以看出,执行步骤:

    1)        outer_wrapper = auth(auth_type="local")

    2)        home = outer_wrapper(home)

    3)home()

    所以这个函数的作用分别是:

    1)        auth(auth_type) 传递装饰器的参数

    2)        outer_wrapper(func) 把函数当做实参传递进来

    3)wrapper(*args,**kwargs) 真正执行装饰的函数

      

  • 相关阅读:
    学习 TList 类的实现[1]
    Dll 使用 PChar 参数的小例子
    学习使用资源文件[11]
    【转载】OGRE 内存管理
    【转载】Ogre的内存分配策略
    【转载】.NET面试题系列[0]
    【转载】深入探讨透视投影坐标变换
    【转载】四元数-Quaterion
    【转载】齐次坐标
    【转载】深入研究Windows内部原理绝对经典的资料
  • 原文地址:https://www.cnblogs.com/Study-Blog/p/7400983.html
Copyright © 2011-2022 走看看