什么是反射?
这里我先说结论:
Python 中的反射其实就是使用字符串调用函数或者方法。
这对于我们设计一些自动化测试框架,特别是类似关键字驱动的时候非常有用。
什么叫用字符串来调用函数或者方法呢?
想想这样一个场景,我通过 excel、数据库或者其他方式记录了自动化测试的操作步骤。其中的一列是对应一段 Python 函数或者方法,另外的列是给这个函数或者方法的参数。
这样有什么好处呢,这样就不用对每一句的测试步骤都去单独编码。
简单示例
我们先以一个非常简单的和测试无关的案例来介绍一下反射的使用。
完成这个案例需要如下几个内容:
- 先有一个 calc.py 文件中编写了几个数学运算函数
- 建立一个 step.yaml 文件,在文件中每行列出要操作的函数和函数的参数
- 最后在 demo.py 文件中定义一段代码,从 step.yaml 数据中读取数据,然后执行对应的代码
通过在 yaml 文件中不断添加数据行,就可以驱动代码执行不同的函数。这很像数据驱动或者关键字驱动的思想。
请务必按照这些代码编写一次,然后观察运行结果,很容易就明白什么意思。如果你偷懒不想去写,只想通过看来搞懂,那请自求多福。
calc.py
def add(*n):
'''累加'''
return sum(n)
def sub(a, b):
'''减法'''
return a - b
def power(a, b):
'''乘方'''
return a ** b
def mult(*n):
'''累乘'''
r = 1
for i in n:
r *= i
return r
def division(a, b):
'''除法'''
return a / b
然后在 step.yaml 文件中定义要操作的函数和数据。
如果对 Yaml 不熟悉, 请参看《YAML 文件介绍》
step.yaml
- method: add
args: [1,2,3,4,5,6]
- method: power
args: [6,7]
- method: sub
args: [8,11]
- method: mult
args: [3,4,5]
- method: division
args: [6,3]
在 yaml 中定义了两个字段:method 用来标识需要调用的函数名 ,args 用来标识函数需要的参数。
接下来在 demo.py 中编写读取 yaml 的代码,并将读取出来的方法和参数通过反射的方式调用 calc.py 中的函数。
import calc # 引入calc.py
import yaml # 引入yaml处理库
with open('step.yml') as f:
data = yaml.load(f, Loader=yaml.SafeLoader) # 读取yaml中的数据
for step in data:
fun = getattr(calc, step['method']) # 使用反射中的getattr访问calc模块中函数
print(fun(*step['args'])) # 执行函数
这里最关键的是这两句代码:
fun = getattr(calc, step['method']) # 使用反射中的getattr访问calc模块中函数
fun(*step['args']) # 执行函数
-
getattr(object, name)
:从对象 oject 中调用 name 属性。因为在 Python 中一切都是对象,所以任何地方都可以用反射函数。这里是从文件(模块)calc.py 中调用其中的函数,函数名是我们从 yaml 文件中读取出来的字符串; -
fun(*step['args'])
:这一句是将参数传给函数,因为 calc.py 中的参数个数是不一样的,所以这里统一读取 yaml 中的 args 字段。
不知你有没有一定明白?就是把字符串(yaml 中的 method 字段)反射到 Python 对象的属性中。
当然这个例子稍微有点歧义的是写的函数都放在一个文件中而不是一个类中,这主要为了方便理解。写在类里面是一样的,只是 getattr() 的第一个参数要写成实例对象。
# 省略类定义的过程
calc = Calc()
getattr(calc, 'add')
反射函数
除了上面的 getattr() 函数外,还有三个函数也是用于反射:
- hasattr(object, name):判断某个对象是否存在 name 属性,name 为字符串
- delattr(object, name):删除某个对象的 name 属性
- setattr(object, name, value):为某个对象设置 name 属性,并通过 value 为属性指定值(值可以是任意数据类型或者函数等)
网友评论