您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Python

如何将 Python 的一个类方法变为多个方法?

时间:2020-01-23 14:51:53  来源:  作者:

作者 | 豌豆花下猫

责编 | 郭芮

Python 中,实现参数化测试的几个库,是如何做到把一个方法变成多个方法,并且将每个方法与相应的参数绑定起来的呢?

我们再提炼一下:在一个类中,如何使用装饰器把一个类方法变成多个类方法(或者产生类似的效果)?

# 带有一个方法的测试类

classTestClass:

deftest_func(self):

pass

# 使用装饰器,生成多个类方法

classTestClass:

deftest_func1(self):

pass

deftest_func2(self):

pass

deftest_func3(self):

pass

Python 中装饰器的本质就是移花接木,用一个新的方法来替代被装饰的方法。在实现参数化的过程中,我们介绍过的几个库到底用了什么手段/秘密武器呢?

ddt 如何实现参数化?

先回顾一下上篇文章中 ddt 库的写法:

importunittest

fromddt importddt,data,unpack

@ddt

classMyTest(unittest.TestCase):

@data(( 3, 1), ( -1, 0), ( 1.2, 1.0))

@unpack

deftest(self, first, second):

pass

ddt 可提供 4 个装饰器:1 个加在类上的 @ddt,还有 3 个加在类方法上的 @data、@unpack 和 @file_data(前文未提及)。

先看看加在类方法上的三个装饰器的作用:

# ddt 版本(win):1.2.1

defdata(*values):

globalindex_len

index_len = len(str(len(values)))

returnidata(values)

defidata(iterable):

defwrApper(func):

setattr(func, DATA_ATTR, iterable)

returnfunc

returnwrapper

defunpack(func):

setattr(func, UNPACK_ATTR, True)

returnfunc

deffile_data(value):

defwrapper(func):

setattr(func, FILE_ATTR, value)

returnfunc

returnwrapper

它们的共同作用是在类方法上 setattr 添加属性。至于这些属性在什么时候使用?下面看看加在类上的 @ddt 装饰器源码:

第一层 for 循环遍历了所有的类方法,然后是 if/elif 两条分支,分别对应 DATA_ATTR/FILE_ATTR,即对应参数的两种来源:数据(@data)和文件(@file_data)。

elif 分支有解析文件的逻辑,之后跟处理数据相似,所以我们把它略过,主要看前面的 if 分支。这部分的逻辑很清晰,主要完成的任务如下:

  • 遍历类方法的参数键值对
  • 根据原方法及参数对,创建新的方法名
  • 获取原方法的文档字符串
  • 对元组和列表类型的参数作解包
  • 在测试类上添加新的测试方法,并绑定参数与文档字符串

分析源码,可以看出,@data、@unpack 和 @file_data 这三个装饰器主要是设置属性并传参,而 @ddt 装饰器才是核心的处理逻辑。

这种将装饰器分散(分别加在类与类方法上),再组合使用的方案,很不优雅。为什么就不能统一起来使用呢?后面我们会分析它的难言之隐,先按下不表,看看其它的实现方案是怎样的?

parameterized 如何实现参数化?

先回顾一下上篇文章中 parameterized 库的写法:

importunittest

fromparameterized importparameterized

classMyTest(unittest.TestCase):

@parameterized.expand([( 3, 1), ( -1, 0), ( 1.5, 1.0)])

deftest_values(self, first, second):

self.assertTrue(first > second)

它提供了一个装饰器类 @parameterized,源码如下(版本 0.7.1),主要做了一些初始的校验和参数解析,并非我们关注的重点,略过。

我们主要关注这个装饰器类的 expand 方法,它的文档注释中写到:

A "brute force" method of parameterizing test cases. Creates new test cases and injects them into the namespace that the wrapped function is being defined in. Useful for parameterizing tests in subclasses of 'UnitTest', where Nose test generators don't work.

关键的两个动作是:“creates new test cases(创建新的测试单元)”和“inject them into the namespace…(注入到原方法的命名空间)”。

关于第一点,它跟 ddt 是相似的,只是一些命名风格上的差异,以及参数的解析及绑定不同,不值得太关注。

最不同的则是,怎么令新的测试方法生效?

parameterized 使用的是一种“注入”的方式:

inspect 是个功能强大的标准库,在此用于获取程序调用栈的信息。前三句代码的目的是取出 f_locals,它的含义是“local namespace seen by this frame”,此处 f_locals 指的就是类的局部命名空间。

说到局部命名空间,你可能会想到 locals,但是,我们之前有文章提到过“locals 与 globals 的读写问题”,locals 是可读不可写的,所以这段代码才用了 f_locals。

pytest 如何实现参数化?

按惯例先看看上篇文章中的写法:

importpytest

@pytest.mark.parametrize("first,second", [(3,1), (-1,0), (1.5,1.0)])

deftest_values(first, second):

assert(first > second)

首先看到“mark”,pytest 里内置了一些标签,例如 parametrize、timeout、skipif、xfail、tryfirst、trylast 等,还支持用户自定义的标签,可以设置执行条件、分组筛选执行,以及修改原测试行为等等。

用法也是非常简单的,然而,其源码可复杂多了。我们这里只关注 parametrize,先看看核心的一段代码:

根据传入的参数对,它复制了原测试方法的调用信息,存入待调用的列表里。跟前面分析的两个库不同,它并没有在此创建新的测试方法,而是复用了已有的方法。在 parametrize 所属的 Metafunc 类往上查找,可以追踪到 _calls 列表的使用位置:

最终是在 Function 类中执行:

好玩的是,在这里我们可以看到几行神注释……

阅读(粗浅涉猎) pytest 的源码,真的是自讨苦吃……不过,依稀大致可以看出,它在实现参数化时,使用的是生成器的方案,遍历一个参数则调用一次测试方法,而前面的 ddt 和 parameterized 则是一次性把所有参数解析完,生成 n 个新的测试方法,再交给测试框架去调度。

对比一下,前两个库的思路很清晰,而且由于其设计单纯是为了实现参数化,不像 pytest 有什么标记和过多的抽象设计,所以更易读易懂。前两个库发挥了 Python 的动态特性,设置类属性或者注入局部命名空间,而 pytest 倒像是从什么静态语言中借鉴的思路,略显笨拙。

小结

回到标题中的问题“如何将一个方法变为多个方法?”除了在参数化测试中,不知还有哪些场景会有此诉求?欢迎留言讨论。

本文分析了三个测试库的装饰器实现思路,通过阅读源码,我们可以发现它们各有千秋,这个发现本身还挺有意思。在使用装饰器时,表面看它们差异不大,但是真功夫的细节都隐藏在底下。

作者:豌豆花下猫,生于广东毕业于武大,现为苏漂程序员,有一些极客思维,也有一些人文情怀,有一些温度,还有一些态度,公众号「Python猫」(python_cat)。

声明:本文系作者投稿,版权归作者所有。



Tags:Python   点击:()  评论:()
声明:本站部分内容来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除,谢谢。
▌相关评论
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
▌相关推荐
什么是数据科学数据科学通常被描述为统计和编程的交集。在本文中,我们讲介绍如何在你的电脑上设置立专业数据科学环境,这样你就可以开始动手实践与流行的数据科学库!什么是...【详细内容】
2020-11-12   Python  点击:(1)  评论:(0)  加入收藏
背景今天双十一,昨晚有好多电商行业的 IT 工程师们挑灯夜战,为这个全民狂欢的购物节护航。还记得三年前我们公司一个产品上线前一周时,办公室内拉起“跟 Bug 死扛到底”的横幅,B...【详细内容】
2020-11-11   Python  点击:(4)  评论:(0)  加入收藏
编写一个 Python 程序,每次下载压缩包形式的文件后,自动将内部文件解压到当前文件夹后将压缩包删除,通过本案例可以学到的知识点:...【详细内容】
2020-11-10   Python  点击:(2)  评论:(0)  加入收藏
> Figure 1: The Monte Carlo Simulation method is used in many industries, from the stock market to finance, energy, banking, and other forecasting models. | So...【详细内容】
2020-11-10   Python  点击:(2)  评论:(0)  加入收藏
python中引号引起来的就是字符串索引索引(下标)通过索引下标可以精确的地位到某个元素 # 变量名未 titl的字符串title = 'python最NB'print(title[0])# 控制台输出 pp...【详细内容】
2020-11-05   Python  点击:(6)  评论:(0)  加入收藏
元素都为真接受一个可迭代对象,如果可迭代对象的所有元素都为真,那么返回 True,否则返回False 元素至少一个为真接受一个可迭代对象,如果可迭代对象里至少有一个元素为真,那么返...【详细内容】
2020-11-05   Python  点击:(6)  评论:(0)  加入收藏
当你学习不熟悉的新东西的时候,一旦发现某样东西有效,那么你就会坚持使用它而放弃探索更多的可能性。在Python中,那样东西就是列表。使用列表的感觉就像是在一直重复你最喜欢的...【详细内容】
2020-11-03   Python  点击:(3)  评论:(0)  加入收藏
有没有想过用python写一个文件管理程序?听起来似乎没思路?其实是可以的,因为python已经为你准备好了神器os.walk,进来看看吧!python中os.walk是一个简单易用的文件、目录遍历器,可...【详细内容】
2020-11-02   Python  点击:(4)  评论:(0)  加入收藏
说一下 python 一般工程的目录结构一般习惯这样规划目录,在开始一个工程前,最好先把目录结构规划好。一、为什么要有一个比较清晰的目录结构此处省略一万字......二、介绍一个...【详细内容】
2020-10-30   Python  点击:(9)  评论:(0)  加入收藏
Python就是把一些参数从一个函数传递到另一个函数,从而使其执行相应的任务。但是你有没有想过,参数传递的底层是如何工作的,原理又是怎样的呢?实际工作中,很多人会遇到这样的场景...【详细内容】
2020-10-29   Python  点击:(5)  评论:(0)  加入收藏
本篇文章介绍了爬虫中验证码的处理方式, 并把这些功能封装起来,供我们使用, 涉及到百度AIP的调用方式, 以及一个最新的开源库muggle识别库的使用,欢迎阅读,点赞,收藏! 目录: 学会调用...【详细内容】
2020-10-29   Python  点击:(4)  评论:(0)  加入收藏
如何理解“栈”?关于“栈”,我有一个非常贴切的例子,就是一摞叠在一起的盘子。我们平时放盘子的时候,都是从下往上一个一个放;取的时候,我们也是从上往下一个一个地依次取,不能从中...【详细内容】
2020-10-28   Python  点击:(6)  评论:(0)  加入收藏
步骤1:基础我们首先学习Python的基础知识——变量、数据类型和操作符。还要学习循环和决策制定等概念: Python Variables Python Variable Scope Data Types in...【详细内容】
2020-10-28   Python  点击:(1)  评论:(0)  加入收藏
前言嗯,我们都知道Zoom是一个视频会议应用程序,它允许我们参加/主持会议。由于新冠的情况,视频会议应用的使用也急剧增加,这成为了一种新的常态,有时这些连续的在线课程变得很麻...【详细内容】
2020-10-20   Python  点击:(7)  评论:(0)  加入收藏
上班都快一周了,一直想更新,奈何小伙还没有从假期的快乐中缓过来,今天终于耐下心来更新一小篇。抠一下某品会的JS代码,接着使用抠取的JS代码加密密码进行登录。友情提示:为避免不...【详细内容】
2020-10-16   Python  点击:(6)  评论:(0)  加入收藏
python的三元表达式相信学过python的朋友都会。但是大部分学python的朋友不知道的是三元表达式还可以嵌套三元表达式。请看代码案例:cmp = lambda a, b: 0 if a == b else...【详细内容】
2020-10-14   Python  点击:(6)  评论:(0)  加入收藏
对程序员来说,Logging 是一种非常重要的功能。无论调试程序还是程序运行时的信息显示,Logging 都很有用。在本文中,我会演示为什么要使用以及如何使用 Python 中的 Logging...【详细内容】
2020-10-14   Python  点击:(9)  评论:(0)  加入收藏
1 说明====1.1 斐波那契数列的介绍。1.2 斐波那契数列是上帝的指纹,大自然中随处可见,目前广泛应用到黄金分割线的布局美和股市等预测等等。1.3 斐波那契数列的python的matplo...【详细内容】
2020-10-12   Python  点击:(25)  评论:(0)  加入收藏
引言Python是一种面向对象的高级动态编程语言,相比于其它如C/C++语言,具有上手快、代码少、开发效率高的特点,Qt是跨平台的C++图形用户界面应用程序开发框架,是当前主流的GUI开...【详细内容】
2020-10-10   Python  点击:(6)  评论:(0)  加入收藏
前言集成开发环境(IDE)允许开发人员用不同的编程语言运行代码,特别地,python IDE实际上是一种IDE,专门让您测试、运行和编辑用python语言编写的代码。当涉及到一个很大的项目时...【详细内容】
2020-10-09   Python  点击:(8)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条