首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

搬砖的也能玩Python-进阶篇3

搬砖的也能玩Python——进阶篇

3-装饰器

回顾

在前面两篇进阶篇中,我们主要对for循环的原理进行展开了解,并从中学到了迭代器、生成器的概念,本篇内容我们将从函数的角度出发,去了解一个叫“装饰器(decorator)”的语法。

一、装饰器的相关概念

首先我们来思考一个问题,在写Web自动化测试脚本的时候,前期只是简单地对比期望结果与实际结果是否一致,当后续完善自动化框架的时候,我们又想当结果不一致时能及时截图保存下来,按照之前学习的内容,我们需要重新修改所有的测试脚本里的方法,加上截图的操作,那么有没有另一种操作,可以让我们不修改测试脚本里的方法,也能在原来的基础上实现截图的功能呢?

答案就是——装饰器(decorator)。而且本人在前段时间搭建自动化测试框架的时候,发现由于使用了assertEqual断言之后,无法通过修改测试脚本的方法添加截图,只能采用装饰器的方式来实现。可见装饰器是很常用的一种语法。

装饰器

装饰器是一个以函数作为参数并返回一个替换函数的可执行函数,说的直白一点,装饰器就是一个函数,这个函数将原来的函数作为参数,返回值是一个新的函数,这个新的函数就是在原来函数的基础上又增加了新的功能。也就是:

def 装饰器名(func):

def 新的函数(*arg, **kwargs):

原来的函数调用前,新增加的功能

result = func(*arg, **kwargs) # 调用原来的函数

原来的函数调用后,新增加的功能

return result

return 新的函数

可能看上面装饰器的结构有些复杂,我们来看一个最简单的装饰器的例子:

在上图的例子中,我们定义了一个decorator1函数,接受一个叫func的参数,这个参数就代表了需要装饰的原来的函数名,在decorator1函数中,我们定义了wrapper函数,用来实现原来的函数功能以及新增加的函数功能,而decorator1函数的返回值就是wrapper,这样就实现了原来函数func的增加其他功能。

再来仔细看一下wrapper函数,参数是*args和**kwargs,表示一个可变参数,因为我们的函数都可以用这个装饰器,而每个函数的参数数量又各不相同,所以python中,用*args和**kwargs表示函数的参数不确定,*args没有key值,**kwargs有key值。在wrapper函数中,我们先找到result = func(*args, **kwargs),这里就是执行原来的函数,在这行代码前后都加了print()方法,就是对原来函数新增加的功能,最后通过return result把原来的函数的结果返回出来给wrapper函数。

二、装饰器的调用

由于装饰器本身也是一个函数,所以可以用函数调用的方式来调用装饰器,也可以在原来的函数定义前面用@装饰器名来调用,我们来分别看一下:

1、函数调用

在上图的例子中,我们先定义了一个add函数,通过调用decorator1()函数,来给add函数增加新的功能,在调用装饰器的时候,我们把add这个函数作为decorator1的参数,此时new_add得到的就是wrapper函数,然后通过new_add(1, 2)来执行wrapper()函数,并且传入原来add函数需要的参数,最终看到的结果就是:执行函数前输入内容、加法计算、函数执行完毕这样的顺序,对于add()函数return出来的结果,我们的new_add也得到了,并且赋值给了s,最终把s打印出来。

2、@装饰器名

上图中看到,我们只需要在定义函数时,加上@装饰器名,就可以实现装饰器的调用,并且此时依旧使用原来的函数名调用即可,不需要介入新的变量名,非常方便,这也是我们最常使用的方法。

三、带有参数的装饰器

因为装饰器也是个函数,函数时可以有参数的,所以我们的装饰器也是支持参数的,而且方式很简单:在上面的装饰函数的外面再包装一层函数,用于传参数。也就是:

def 装饰器名称(装饰器参数):

def 新的函数1(func):

def 新的函数2(*arg, **kwargs):

原来的函数调用前,新增加的功能

可以使用装饰器参数

result = func(*arg, **kwargs) # 调用原来的函数

原来的函数调用后,新增加的功能

return result

return 新的函数2

return 新的函数1

我们来看一个简单的例子,我们在之前decorator1的基础上,来实现求加法结果的任意倍数的功能,这个倍数就是我们装饰器的一个参数。

在这个装饰器中,我们在外面加了一层decorator2函数,并且设置了参数mul_num,在wrapper函数中,我们用到了mul_num参数用于倍数的计算,这就是带参数的装饰器。

在使用带参数的装饰器时,我们只需要在@装饰器名后面加上参数即可。

从上图中不难看出,在定义add()函数时,我们使用了@decorator2(mul_num=3),给mul_num赋值3之后,从最终的结果中我们也看到这个参数被正确使用。

四、装饰器作为类

前面说的装饰器实质上都是函数,其实装饰器也可以作为类,依靠类内部的__call__()方法,在函数前面用@类名即可:

class 装饰器类名:

def __init__(self, func):

self.func = func

def __call__(*args, **kwargs):

原来的函数调用前,新增加的功能

result = self.func(*arg, **kwargs) # 调用原来的函数

原来的函数调用后,新增加的功能

return result

我们来看一个简单的例子:

从上图结果可以看出,当调用类装饰器时,会先进行类的初始化,初始化完成之后,才会调用__call__()方法进行装饰。

五、自动化框架中的装饰器

本篇文章的开头,我们提到自动化框架中通过装饰器来实现测试不通过时的截图,我们来简单看一下是如何实现的,具体的内容在自动化框架的文章中会有了解,敬请期待。

在unittest测试框架中,我们通过assert断言来验证实际结果和期望结果,由于assert方法在判断的时候,结果如果是fail的话,是不会再执行后面的代码,所以想直接测试不通过截图的话,需要用try...except的异常处理语句来实现,但由于测试脚本非常多,验证点也非常多,不可能每一个都重新用try...except来写,所以就用装饰器的方法,创建对应的装饰器即可。见下图:

通过上图中的装饰器,我们实现了测试不通过是的截图功能,我们只需要在assert方法前面加上@装饰器即可,如下图:

探测八个蛋∣跳出手工测试的井

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180304G123TX00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券