zoukankan      html  css  js  c++  java
  • Python 闭包 装饰器

     

    目录 

    1.基础知识点

    2.闭包

    3.装饰器

    正文

    1.基础知识点------函数可以作为参数传给另一个函数

    #将函数作为参数传给另一个函数
    def test1(name):
        print("函数test1")
        return name()
    
    def test2():
        print("函数test2")
    
    test1(test2)
    """
    函数test1
    函数test2
    """

    调用函数test1(test2),传入的参数是函数test2

    注意test2作为参数,不能加(),才可以被到处传递。如果加了括号,就是调用test2函数了。

    例如,下面的test2加括号,就是先调用test2()函数,再执行test1()函数了。

    def test1(name):
        print("函数test1")
    
    def test2():
        print("函数test2")
    
    test1(test2())
    """
    函数test2
    函数test1
    """

    2.闭包  Closure

    ①什么是闭包?

     ----嵌套函数:即外层函数里面又定义了一个内层函数

     ----内层函数调用了外层函数的非全局变量

     ----外层函数的返回值:是内层函数名

    以上3个条件同时满足,就构成了一个闭包。

    这么说有点抽象,举个栗子:

    def outer(a):
        print("这是外层函数")
        def inner():
            b=a+1
            print("这是内层函数")
            print(b)
        return inner
    
    outer(3)()

    """

    这是外层函数
    这是内层函数
    4

    """

    调用outer(3)得到的结果是打印【这是外层函数】并返回inner ;

    outer(3)()就是在outer(3)的基础上再次调用内层函数,得到最后的结果。

    上面的知识都基本理解了,下面讲解重点内容:装饰器

    3.装饰器

    ①什么是装饰器?

    装饰器本身也是一个函数或者类,可以新增一些被装饰函数/类的功能,简化代码。

    ②装饰器的分类:上面的定义可知,有函数装饰器 、类装饰器---------(被装饰的对象可以是函数,也可以是类)

    注意:装饰器来装饰函数或者类,被装饰的函数或类的原功能是不变的,会增加一些新的功能;

               其次,被装饰的函数或者类的调用方式是不会变的-----切记

    装饰器的语法糖

        被装饰的函数/类名 = 装饰器名(被装饰的函数/类名)

    怎么理解这句话,来看个栗子:

    #装饰器原理
    def fun1(func): def fun2(): print("执行func()函数之前") func() print("执行func()函数之后") return fun2
    def fun3(): print("这是功能函数") fun3() print("-------分割线---------") fun3=fun1(fun3) #被装饰的函数 = 装饰器名(被装饰的函数名) fun3() """ 这是功能函数 -------分割线--------- 执行func()函数之前 这是功能函数 执行func()函数之后 """

    函数装饰器实例1-----被装饰的函数,无参数

    def login(a):
        print("这是登录函数")
        def recharge():
            a()
            print("这是充值函数")
        return recharge
    
    @login
    def withdraw():
        print("这是取现函数")
    
    
    withdraw()
    """
    登录函数
    取现函数
    充值函数
    """
    怎么理解上面的调用步骤:

    根据语法糖公式:只要withdraw函数被装饰,那么@login 就等价于 withdraw = login(withdraw) ====》输出 "登录函数” 和 返回recharge变量

    withdraw() = login(withdraw)()==recharge()=====>recharge()函数中的a()就是withdraw(),最后的结果如上。

    函数装饰器实例2-----被装饰的函数,有参数

    还是上面的装饰函数login()

    def login(a):
        print("这是登录函数")
        def recharge():
            a()
            print("这是充值函数")
        return recharge

    要对下面的函数withdraw(p,q)进行装饰,应该怎么修改上面的装饰函数?

    def withdraw(p,q):
        print("这是取现函数")

    修改步骤:

    首先要装饰withdraw(p,q),先在函数上面添加@login;

    那么,根据装饰器的语法糖公式:withdraw = login(withdraw)=recharge ;

    要调用withdraw() ,那么withdraw(p,q) = login(withdraw)(p,q) =recharge(p,q)

    所以,只要修改内层函数为recharge(j,k)即可完成装饰函数的修改。

    #2.被装饰函数有参数
    def login(a):
        print("这是登录函数")
        def recharge(j,k):
            print("这是充值函数")
            return a(j,k)
    
        return recharge
    
    @login
    def withdraw(p,q):
        print("这是取现函数")
        return p+q
    
    print(withdraw(3,4))
    """
    这是登录函数
    这是充值函数
    这是取现函数
    7
    """

    在实际项目应该中,参数可能有0个或者多个,那么就用到了可变长参数(动态参数):

    元祖形式的可变长参数*args,   和字典形式的**kwargs,如下

    添加计时器功能(自动化中用计时器的功能非常多,在这里提及一下)

    #2.被装饰函数有参数
    import time
    def login(a):
        print("这是登录函数")
        def recharge(*args,**kwargs):
            time_before =time.time()
            res= a(*args,**kwargs)
            time_after = time.time()
            ti = time_after-time_before
            print(ti) #计时器功能
            print("这是充值函数")
            return res
    
        return recharge
    
    @login
    def withdraw(p,q):
        print("这是取现函数")
        return p+q
    
    
    print(withdraw(3,4))
    """
    这是登录函数
    这是充值函数
    0.0
    这是取现函数
    7
    """

    函数装饰器实例3-----被装饰的类,无参数

    def login(a):
        print("这是登录函数")
        def recharge():
            a()
            print("这是充值函数")
           
        return recharge

    还是上面login这个函数装饰器,要对一个类进行装饰,应该怎么修改装饰器?

    @login
    class
    MyClass: def withdraw(self): print("这是取现函数")

    首先,在类的上面添加@login

    类里面的实例方法的调用,首先要初始化一个对象MyClass(),对象再去调用里面的方法,即MyClass().withdraw()

    同理,语法糖公式:MyClass = login(MyClass)=recharge 

    那么 ,要初始化对象MyClass(),则MyClass()=login(MyClass)()=recharge (),即最后是调用recharge()函数,就是要修改内层函数。

    那么怎么修改?最后要实现MyClass().withdraw(), 所以recharge()函数的返回值要是一个MyClass()对象,也就是return a() -----a是login的形参,装饰后传入的就是MyClass

    #3被装饰的是一个类
    def login(a):
        print("这是登录函数")
        def recharge():
            print("这是充值函数")
            return a()
    
        return recharge
    
    @login   #MyClass() = login(MyClass)() =recharge()
    class MyClass:
        def withdraw(self):
            print("这是取现函数")
    
    MyClass().withdraw()
    """
    这是登录函数
    这是充值函数
    这是取现函数
    """

    ⑤类装饰器实例1---装饰函数

    魔术方法 :__call__(),只有在类的对象加上括号的时候,才会触发

    举个栗子:

    class MyClass:
        def __call__(self,*args,**kwargs):
            print("这个是魔术方法")
    
    
    MyClass()()  #类的对象加上括号,触发call方法
    #这个是魔术方法

    那么,由上面的类,给下面的函数装饰,应该怎么修改类装饰器呢?

    ⑤类装饰器

    @MyClass
    def fun():  
        print("功能函数")

    函数的调用就是fun()

    公式: fun=MyClass(fun) -->则类里面要传个参数啊----》参数哪里传?__init__()传参数啊。即类里面添加一个__init__()进行初始化

    又 fun()=MyClass(fun)(),即MyClass(fun)()对象又加一个括号,会触发call方法,所以要在call方法中去调用被装饰的函数,就可以实现不影响原功能函数的功能,又可以新增功能啦

    class MyClass:
    
        def __init__(self,a):
            self.a = a
    
        def __call__(self,*args,**kwargs):
            self.a()
            print("这个是魔术方法")
    
    
    @MyClass
    def fun():  #fun()=MyClass(fun)()
        print("功能函数")
    
    fun()
    """
    功能函数
    这个是魔术方法
    """

    ⑤类装饰器实例2---装饰类

    class MyClass:
    
        def __init__(self,a):
            self.a = a
    
        def __call__(self,*args,**kwargs):
            print("这个是魔术方法")
            return self.a()
    
    
    @MyClass  #B()=MyClass(B)()
    class B:
        def fun(self):
            print("B类的实例方法")

    类B中的方调用是B().fun()

    B()=MyClass(B)(),触发call()方法,不影响原来的类的调用,在call中,直接将a()返回即可。

    ⑥对装饰器进行传参----定义三层函数,最外层函数的返回值是第二层函数名

    在接口自动化中,数据驱动ddt,就是装饰器传参,对参数做了一系列的处理,将每条数据传入内层函数中。

    下面来探究装饰器传参的原理。

    def var(name):
        def test1(a):
            print("test1函数")
            def test2(b):
                for i in name:
                    a(i)
                print("test2函数")
            return test2
        return test1
    
    @var([1,2,3])  #var([1,2,3])=test1 ---> fun()=test1(fun)()=test2(b)
    def fun(b):
        print("正在执行第{}条测试用例".format(b))
    
    fun(2)
    """
    test1函数
    正在执行第1条测试用例
    正在执行第2条测试用例
    正在执行第3条测试用例
    test2函数
    """

    装饰器传参数,在最外层函数传参。

    @var([1,2,3])传入参数是列表[1,2,3],  var([1,2,3])=test1,按照装饰器的语法糖,在内层函数中调用了a(),就是调用fun().

    在这个基础上,增加了for循环。-----仿照ddt数据驱动

    ⑦多个装饰器的执行顺序-----------原理从下往上,执行从上往下

    def test1(fun1):
        def wrapper1():
            fun1()
            print("装饰器1")
        return wrapper1
    
    def test2(fun2):
        def wrapper2():
            fun2()
            print("装饰器2")
        return wrapper2
    
    @test1     #wrapper2=test1(wrapper2)
    @test2     #fun=test2(fun)=wrapper2
    def fun():
        print("功能函数")
    
    fun()

    """

    功能函数
    装饰器2
    装饰器1

    """

     

    原理讲解:fun()函数被test1()、test2()装饰,按照原理从下往上,fun函数先被test2()装饰,再被test1装饰时,test1装饰的其实就是wrapper2

    装饰原理从下往上,写出公式。

    @test1     #wrapper2=test1(wrapper2)
    @test2     #fun=test2(fun)=wrapper2


    执行按照从上往下:wrapper2()=test1(wrapper2)()
    首先wrapper2()的执行结果是:
    功能函数
    装饰器2
    再看,test1装饰wrapper2,即test1中转入的参数是wrapper2,最后输出的结果如上代码的输出结果

    ⑧装饰器的应用

    1).计时          装饰器中实现计时功能,在自动化测试中,可以用来计时(装饰器中实现计时、调用原函数、计时,计算时差,实现计时功能)

    2).前置、后置条件。装饰器中实现前置条件(比如打开浏览器),再调用原函数,实现后置条件(关闭浏览器)-----装饰器中调用原函数的前后各实现钱之后只条件即可

    3).鉴权 比如登录 充值

       关联:只有先登录-->才能进行充值

     装饰器函数或者类,实现登录功能,获取token/session; 被装饰的充值函数,会自动获取token/session

     举个栗子(写个大致的过程):

    #导入session类
    from requests import Session
    
    #初始化一个session对象
    s=Session()
    
    #装饰器-实现登录功能
    def login_success(fun):
        URL = "www.douban.com"
        json={"username":"kkk","password":"123456"}
        method = "post"
        def wrapper():
            s.post(url=URL,json=json,method=method)
            fun()
        return wrapper
    @login_success
    def recharge(): res=s.post()

    上面的例子,s这个对象属于全局变量,闭包使用的是非全局变量,那么可以用装饰器传参数的方式,将Session()对象作为参数,这样的话,闭包内部函数引用的就是外层函数的非全局变量。

    如下:登录和充值是同一个session会话。-----登录装饰器给充值用例装饰

    #导入session类
    from requests import Session
    #装饰器-实现登录功能
    def var(s):
        def login_success(fun):
            URL = "www.douban.com"
            json={"username":"kkk","password":"123456"}
            method = "post"
            def wrapper():
                s.post(url=URL,json=json,method=method)
                fun()
            return wrapper
        return login_success
    
    @var(Session())   #=@login_success
    def recharge():
        res=s.post()

        

     





  • 相关阅读:
    Testdisk 操作指南(硬盘分区表恢复)
    ThinkPHP下使用Uploadify插件提示HTTP Error (302)错误的解决办法
    C#获取计算机CPU的温度
    C# 获取显示器的物理尺寸或分辨率
    获取windows 操作系统下的硬件或操作系统信息等
    AD CS relay attack
    内网密码收集[Foxmail]解密
    如果你是业务线的程序员
    浅析php curl_multi_*系列函数进行批量http请求
    memcached讲解
  • 原文地址:https://www.cnblogs.com/ananmy/p/13602964.html
Copyright © 2011-2022 走看看