目录
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()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律