装饰器之前,先了解闭包
1、闭包
- 闭包介绍及作用
1、当函数调用完,内部的变量就会销毁
2、闭包可以保存外部函数的变量,不会随着外部函数调用完而销毁
3、如果需要使用函数内的变量,或者在变量的基础上完成相应的操作:例如:每次在这个变量的基础上和其他数据进行求和计算 - 构成定义
1、在函数嵌套的前提下,内部函数使用外部函数的变量,并且外部函数返回了内部函数
2、使用外部函数变量的内部函数称为闭包 - 闭包构成条件
1、函数嵌套(函数中嵌套函数)
2、内部函数使用外部函数的变量,也或者是参数
3、外部函数返回了内部函数
# 外部函数out
def out(num1):
# 内部函数inner
def inner(num2):
# 内部函数使用了外部函数的变量(num1)
result = num1 + num2
print("result:", result)
# 外部函数返回了内部函数,返回的内部函数就是闭包
return inner
# 创建闭包实例
res = out(10)
# 执行闭包
res(2)
res(3)
# 执行结果
result: 12
result: 13
2.1、装饰器使用
- 装饰器使用
1、本质是一个闭包函数
2、给已有函数增加额外功能的函数,例:log打印信息
3、特点:
~~1、不修改已有函数的源代码
~~2、不修改已有函数的调用方式
~~3、给已有函数增加额外的功能
# 添加日记打印信息
def log(func):
def inner():
print("这是一条日志信息")
func()
return inner
#一个登录测试用例
def test_login():
print("测试登录")
# 使用装饰器来装饰函数
test_login = log(test_login)
test_login()
- 注意: 闭包函数有且只有一个参数,必须是函数类型,这样定义的函数才是装饰器
- 装饰器写法
- 装饰无参数的函数
# 添加日记打印信息
def log(func):
def inner():
print("这是一条日志信息")
func()
return inner
#一个登录测试用例
@log #test_login = log(test_login)
def test_login():
print("测试登录")
# 使用装饰器来装饰函数
test_login()
- @log等同于test_login = log(test_login)
- 装饰有参数的函数
# 输出日志
def log(func):
def inner(num1, num2):
print("--正在努力计算--")
func(num1, num2)
return inner
# 装饰器装饰函数
@log
def test_sum(num1, num2):
result = num1 + num2
print(result)
test_sum(1, 2)
- 注意:内部函数inner中的参数要与test_sum函数的参数一致
- 装饰带有返回值的函数
# 输出日志
def log(func):
def inner(num1, num2):
print("--正在努力计算--")
result = func(num1, num2)
return result
return inner
# 装饰器装饰函数
@log
def test_sum(num1, num2):
result = num1 + num2
return result
result = test_sum(1, 2)
print(result)
- 注意内部函数inner中的func调用要与test_sum一致
- test_sum中有返回值,故inner中的fund()需要有返回值 ,并且inner要return
- 装饰带有不定长参数的函数
# 添加输出日志的功能
def log(func):
def inner(*args, **kwargs):
print("--正在努力计算--")
func(*args, **kwargs)
return inner
# 使用语法糖装饰函数
@log
def test_sum(*args, **kwargs):
result = 0
for value in args:
result += value
for value in kwargs.values():
result += value
print(result)
test_sum(1, 2, value=10)
- 注意:参数可使用位置参数*args、关键字参数**kwargs
- 通用装饰器总结
# 通用装饰器
def log(func):
def inner(*args, **kwargs):
print("")
return func(*args, **kwargs)
return inner
2.2 多个装饰器使用
- 多个装饰器的装饰过程是: 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程
def decorator1(func):
def wrapper(*args, **kwargs):
print('decorator1')
return func(*args, **kwargs)
return wrapper
def decorator2(func):
def wrapper(*args, **kwargs):
print('decorator2')
return func(*args, **kwargs)
return wrapper
@decorator2
@decorator1
def get(self):
return {'msg': 'get view'}
2.3 带有参数的装饰器
- 带有参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数,语法格式: @装饰器(参数,…)
- 由于装饰器只能接口一个参数,并且是函数类型,所以在原基础上,外部增加1个函数来实现
def log(flag):#新增加函数
#原装饰器函数--------start
def decorator(func):
def inner(*args,**kwargs):
if flag == "+":
print("加法")
elif flag == "-":
print("减法")
func(*args,**kwargs)
return inner
#原装饰器函数-------end
return decorator#新增加函数返回装饰器
@log("+")
#装饰log开始时,相当于decorator=log("+") ,返回decorator装饰器后相当于 @decorator
#@decorator相当于 最开始的装饰器 add = decorator(add),add相当于返回了inner这个内部函数
def add(num1,num2):
print(num1+num2)
@log("-")
def sub(num1,num2):
print(num1-num2)
add(1,2)
sub(2,1)
- 总结:使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回是装饰器,因为 @ 符号需要配合装饰器实例使用
3 类装饰器
就是通过定义一个类来装饰函数
class MyDecorator:
def __init__(self,func):
self.func = func
def __call__(self, *args, **kwargs):
print("MyDecorator")
self.func()
@MyDecorator
def func_print():
print("func_print")
func_print()
- @MyDecorator相当于 func_print = MyDecorator(func_print),这里会发现MyDecorator类实例化时需要参数,所以在类里面我们需要定义__init__函数来实现
- func_print = MyDecorator(func_print)
func_print() - 这里会发现func_print其实是个实例,实例一般是调用方法,并不能直接运行,所以我们需要在类里面定义__call__函数来实现实例直接运行
- 并且需要执行func_print(),也就是传入的参数func,所以需要在__init__函数里面声明属性,然后在__call_函数里面是执行func()
3.2 装饰的函数在类里面的调用
- 直接@log即可
def log(func):
def inner(*args, **kwargs):
print("--日志登录为--")
func(*args, **kwargs)
return inner
class TestLogin:
@log
def test_run(self):
print("test_run")
- 装饰器中需要调用 使用类的属性
- 由于类中的方法使用默认是带self,所以在定义装饰器中,增加self的方式来实现
class Operate():
def __init__(self,driver):
self.driver = driver
@screenshot_allure
def step(self,data,run_case):
tc_id = run_case[TestSteps.STEP_TC_ID]
#获取步骤
steps = data.get_steps_by_tc_id(tc_id)
#allure报告
#feature
allure.dynamic.feature(run_case[TestCases.CASES_NOTE])
#story
allure.dynamic.story(run_case[TestCases.CASES_DESC])
#title
allure.dynamic.title(run_case[CaseData.DATA_CASE_ID]+"-"+run_case[CaseData.DATA_NAME])
#1、定义装饰2层函数
def screenshot_allure(func):
def get_err_screenshot(self,*args,**kwargs):
#2、定义内部函数,拍图操作,注意self,这里是一个参数对象
try:
func(self,*args,**kwargs)
except Exception as e:
png = self.driver.get_screenshot_as_png()
name = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
allure.attach(png, name, allure.attachment_type.PNG)
raise e
#3、返回内部函数名称
return get_err_screenshot
- 注意:get_err_screenshot(self,*args,**kwargs):中增加了self这个参数,与类中方法 def step(self,data,run_case):保持一致性