想用Python做自动化测试?Python反射机制的应用!

通常,我们操作对象的属性或者方法时,是通过点“.”操作符进行的。例如下面的代码:

class Person:    type = "mammal"
    def __init__(self, name):        self.name = name
    def say_hi(self):        print('Hello, my name is', self.name)
    @staticmethod    def feed():        print("Three times per day.")
    @classmethod    def sleep(cls):        print("8 hours!")p = Person('Chunming')p.say_hi()print(p.name)

上面代码的输出是:

Hello, my name is NikhilNikhil

反射是另外一种操作对象属性和方法的手段,例如:

func = getattr(p, 'say_hi') func()print(getattr(p, "name"))

上面这段代码的输出是:​​​​​​​

Hello, my name is NikhilNikhil

可见与通过点操作符的结果一致。

1. 反射的四个函数

getattr是获取对象属性或方法的函数,Python的官方文档是这样描述其用法的:

getattr(object, name, value)

返回对象命名属性的值。name必须是字符串。如果该字符串是对象的属性之一,则返回该属性的值。例如, getattr(x, 'foobar')等同于 x.foobar。如果指定的属性不存在,且提供了 default值,则返回它,否则触发 AttributeError。

根据文档理解上述代码,getattr(p, 'say_hi') 获取了p对象的say_hi属性值并赋值给func变量,因为say_hi属性在Person类中是一个方法,要想调用这个方法, 需要执行func()getattr(p, "name") 则是获取p对象的name属性。

除了获取对象属性和方法的getattr函数,python还内置了判断、设置、删除对象属性和方法的函数,来看看Python官方文档对这三个函数的说明:

setattr(object, name, value)

此函数与 getattr() 两相对应。其参数为一个对象、一个字符串和一个任意值。字符串指定一个现有属性或者新增属性。函数会将值赋给该属性,只要对象允许这种操作。例如,setattr(x, 'foobar', 123) 等价于 x.foobar = 123

hasattr(object, name)

该实参是一个对象和一个字符串。如果字符串是对象的属性之一的名称,则返回 True,否则返回 False。(此功能是通过调用 getattr(object, name) 看是否有 AttributeError 异常来实现的。)

delattr(object, name)

setattr() 相关的函数。实参是一个对象和一个字符串。该字符串必须是对象的某个属性。如果对象允许,该函数将删除指定的属性。例如 delattr(x, 'foobar') 等价于 del x.foobar

Python中通过getattr、setattr、hasattr和delattr四个函数操作属性的机制就是反射。是通过字符串的形式操作对象属性和方法的机制。下面对p属性应用setattr、hasattr和delattr这三个函数看看效果:

判断p对象是否有say_bye属性和say_hi属性:​​​​​​​

print(hasattr(p, 'say_bye'))  # 输出Falseprint(hasattr(p, 'say_hi'))  # 输出True

给p对象增加say_bye的方法和age属性:​​​​​​​

setattr(p, 'say_bye', say_bye)setattr(p, 'age', 18)

现在可以访问这两个属性了,通过反射访问:​​​​​​​

bye = getattr(p, 'say_bye')bye()print(getattr(p, 'age'))

或者通过点操作符访问:​​​​​​​

p.say_bye()print(p.age)

删除age属性:​​​​​​​

delattr(p, 'age')print(hasattr(p, 'age'))  # 输出False
现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:691998057【暗号:csdn999】

2. 类的反射操作

除了对象的反射操作,还有类的反射操作,当前模块的反射操作,还有其他模块的反射操作,其他包的反射操作。

类的反射操作,指的是对类属性、类方法或者静态方法执行反射操作。

获取类属性:​​​​​​​

t = getattr(Person, 'type')print(t)  # 输出mammalf = getattr(Person, 'feed')f()  # 输出Three times per day.s = getattr(Person, 'sleep')s() # 8 hours!

判断类属性是否存在:​​​​​​​

print(hasattr(Person, 'type'))  # 输出Trueprint(hasattr(Person, 'name'))  # 输出Falseprint(hasattr(Person, 'say_hi')) # 输出Trueprint(hasattr(Person, 'sleep'))  # 输出Trueprint(hasattr(Person, 'feed'))  # 输出True

此外,还可以对类添加和删除属性和方法。​​​​​​​

print(delattr(Person, 'feed'))print(hasattr(Person, 'feed'))setattr(Person, 'feed', lambda x: print("Three times per day."))print(hasattr(Person, 'feed'))

3. 当前模块的反射操作

当前模块也就是当前代码所在的py文件,反射也可以对当前模块中的变量和函数进行操作。例如某个模块包含一个al函数,用来判断迭代对象中每个元素是否都是True,内容如下:​​​​​​​

import sys
def al(iterable):    for element in iterable:        if not element:            return False    return Truethis_module = sys.modules[__name__]
if hasattr(this_module, 'al'):    all_true = getattr(this_module, 'al')    result = all_true([1, 2, 3, 4, True, 0])    print(result)

通过sys.modules[__name__]方法获取当前模块的名称。getattr第一个参数是模块名称,第二个参数是想要从模块中获取的属性。

4. 其他模块反射操作

对import进来的其他模块中的函数、属性、变量进行反射操作。例如,我们导入Python的heapq模块,这块模块提供了堆队列算法的实现,也称为优先队列算法。下面的代码是一个实现堆排序的函数。

import heapqh = [(5, 'write code'), (7, 'release product'), (1, 'write spec'), (3, 'create tests')]if hasattr(heapq, 'heapify'):heapi = getattr(heapq, 'heapify')  # 获取heapify属性heapi(h)  # 建堆if hasattr(heapq, 'heappop'):heapp = getattr(heapq, 'heappop')  # 获取heappop属性print([heapp(h) for _ in range(len(h))])  # 弹出并从返回堆中最小的项

这里,我们并没有通过heapq.heapifyheapq.heappop方式调用heapq模块中的函数。而是通过反射达到的同样的效果。

5. 反射应用场景之一

了解了反射中四个函数的基本用法。那么反射到底有什么用呢?它的应用场景是什么呢?答案是,当不确定所需要的属性和函数是否存在时,可以使用反射。另外一个重要作用是,可以提高代码的扩展性和可维护性。

假如我们把所有的加密算法都放到一个叫做encryption的模块中维护 ,并且允许使用这个模块的用户添加更多的加密算法到这个模块中。encryption的模块内容如下:​​​​​​​

import hashlibimport osimport sysdef md5(content=None):    """生成字符串的SHA256值"""    if content is None:        return ''    md5_gen = hashlib.md5()    md5_gen.update(content.encode('utf-8'))    md5code = md5_gen.hexdigest()    return md5codedef sha256(content=None):    """生成字符串的SHA256值"""    if content is None:        return ''    sha256_gen = hashlib.sha256()    sha256_gen.update(content.encode('utf-8'))    sha256code = sha256_gen.hexdigest()    return sha256codedef sha256_file(filename):    """生成文件的SHA256值"""    if not os.path.isfile(filename):        return ""    sha256gen = hashlib.sha256()    size = os.path.getsize(filename)  # 获取文件大小,单位是Byte    with open(filename, 'rb') as fd:  # 以二进制方式读取文件        while size >= 1024 * 1024:  # 当文件大于1MB时分块读取文件内容            sha256gen.update(fd.read(1024 * 1024))            size -= 1024 * 1024        sha256gen.update(fd.read())    sha256code = sha256gen.hexdigest()    return sha256codedef md5_file(filename):    """生成文件的MD5值"""    if not os.path.isfile(filename):        return ""    md5gen = hashlib.md5()    size = os.path.getsize(filename)  # 获取文件大小,单位是Byte    with open(filename, 'rb') as fd:        while size >= 1024 * 1024:  # 当文件大于1MB时分块读取文件内容            md5gen.update(fd.read(1024 * 1024))            size -= 1024 * 1024        md5gen.update(fd.read())    md5code = md5gen.hexdigest()    return md5codedef encrypt_something(something, algorithm):    """    通用加密算法    :param something: 待加密的内容,字符串或者文件    :param algorithm: 加密算法    :return:  加密后的内容    """    result = ""    if algorithm == "md5":        result = md5(something)    elif algorithm == "sh256":        result = sha256(something)    elif algorithm == "sh256_file":        result = sha256_file(something)    elif algorithm == "md5_file":        result = md5_file(something)    return result

其中,encrypt_something函数提供了通用加密算法,需要调用者传入待加密的内容和加密算法,这样当调用者使用encryption.py模块时,只需导入encrypt_something函数即可。就像这样:​​​​​​​

import encryptionprint(encryption.encrypt_something("learn_python_by_coding", "sh256"))print(encryption.encrypt_something("learn_python_by_coding", "md5"))

通过分析encrypt_something函数发现,当我们在encryption.py模块添加更多的加密算法后,就要修改encrypt_something函数,在其中增加新的if分支,随着加密算法的增加,encrypt_something函数的分支会越来越多。

学了反射之后,encrypt_something代码部分就可以这样写:​​​​​​​

def encrypt_something(something, algorithm):    """    通用加密算法    :param something: 待加密的内容,字符串或者文件    :param algorithm: 加密算法    :return:  加密后的内容    """    this_module = sys.modules[__name__]    if hasattr(this_module, algorithm):        algorithm = getattr(this_module, algorithm)        result = algorithm(something)    else:        raise ValueError("Not support {} algorithm".format(algorithm))    return result

相比前面的采用if分支语句方式,反射更加简洁明了,可维护性更强,要想增加新的加密方法,只需要在encryption.py模块添加更多的加密算法即可,encrypt_something代码不需要任何变更。

6. 反射应用场景之二

再看一个基于Pytest测试框架的测试脚本中应用反射的例子,比如conftest文件内容如下:​​​​​​​

# content of conftest.pyimport pytestimport smtplib@pytest.fixture(scope="module")def smtp_connection(request):    server = getattr(request.module, "smtpserver", "smtp.gmail.com")    smtp_connection = smtplib.SMTP(server, 587, timeout=5)    yield smtp_connection    print("finalizing {} ({})".format(smtp_connection, server))    smtp_connection.close()

request.module 是所有测试脚本,就是那些以_test结尾或者test_开头的py文件。

server = getattr(request.module, "smtpserver", "smtp.gmail.com") 

含义就是从测试脚本文件中找smtpserver属性,如果找不到,默认使用smtp.gmail.com作为smtpserver的值。如果测试脚本文件有这个属性,则使用测试脚本中的值,例如下面这个测试脚本,smtpserver则会使用mail.python.org这个值:​​​​​​​

# content of test_anothersmtp.py
smtpserver = "mail.python.org"  # will be read by smtp fixturedef test_showhelo(smtp_connection):    assert 0, smtp_connection.helo()

7. 总结

在很多开源框架中普遍采用,是提高可维护性和扩展性的利器。如果工作中也涉及到框架开发,反射一定会助一臂之力。以上就是我目前对于Python反射的理解,后续遇到更多的案例,我也会持续更新进来。

下面是配套资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!

最后: 可以在公众号:自动化测试老司机! 免费领取一份216页软件测试工程师面试宝典文档资料。以及相对应的视频学习教程免费分享!,其中包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。

如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/520155.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

006-浏览器输入域名到返回

浏览器输入域名到返回 1、URL 输入2、DNS 域名解析3、建立 TCP 连接三次握手概念三次握手理解 4、发送 HTTP/HTTPS 请求5、服务器处理,并返回响应6、浏览器解析并渲染页面7、请求结束,端口 TCP 连接四次挥手概念四次挥手理解 1、URL 输入 2、DNS 域名解析…

[HackMyVM]靶场 Wild

kali:192.168.56.104 主机发现 arp-scan -l # arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:d2:e0:49, IPv4: 192.168.56.104 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.56.1 0a:00:27:00:00:05 …

20240307-2-前端开发校招面试问题整理HTML

前端开发校招面试问题整理【2】——HTML 1、HTML 元素&#xff08;element&#xff09; Q&#xff1a;简单介绍下常用的 HTML 元素&#xff1f; 块状标签&#xff1a;元素独占一行&#xff0c;可指定宽、高。 常用的块状元素有&#xff1a; <div>、<p>、<h1&…

横琴正式封关运行,惟客数据都做了什么?

​作为中国实施高水平制度型开放的重大探索&#xff0c;位于珠海横琴岛的横琴粤澳深度合作区于2024年3月1日零时正式实施分线管理封关运行&#xff0c;共设1个“一线”口岸、7个“二线”海关作业现场&#xff0c;覆盖旅检、货运、通关、稽&#xff08;核&#xff09;查等多线条…

火柴排队(逆序对 + 离散化)

505. 火柴排队 原题链接 思路 如下是画图分析的一些过程 在这里贪心的思路是排序&#xff0c;然后两个数组都是从小到大那样对应的话最终的答案可达到最小 而我们只能交换相邻的火柴&#xff0c;故在这里先假设一个简化版本&#xff0c;即A有序&#xff0c;而只需要对B进行…

【数据结构五】队列和Queue详解

目录 队列 1.模拟实现一个队列 2.Queue的基本使用 3.循环队列详解 4.双端队列详解 5.分别用栈实现队列&#xff0c;队列实现栈 队列 队列 &#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出 FIFO(Fi…

ruoyi-vue框架密码加密传输

先看一下改造后的样子&#xff0c;输入的密码不会再以明文展示。 下面我主要把前后端改造的代码贴出来。 1.后端代码 RsaUtils类 在com.ruoyi.common.utils包下新建RsaUtils类&#xff0c;RsaUtils添加了Component注解 generateKeyPair()构建密钥对添加了Bean注解 在项目启动…

java 获取项目内的资源/配置文件

【getResourceAsStream】是java中用于获取项目内资源的常用方法&#xff0c;能够返回一个数据流&#xff0c;从而允许我们读取指定路径下的资源文件。这个方法可以用来读取各种类型的资源文件&#xff0c;包括但不限于文本文件、图像文件、配置文件等。 要使用getResourceAsStr…

Ansible 基础入门

2&#xff09;Ansible 介绍 Ansible 基本概念 Ansible 是一种自动化运维工具&#xff0c;基于 Paramiko 开发的&#xff0c;并且基于模块化工作&#xff0c;Ansible 是一种集成 IT 系统的配置管理、应用部署、执行特定任务的开源平台&#xff0c;它是基于 Python 语言&#xf…

19-Java中介者模式 ( Mediator Pattern )

Java中介者模式 摘要实现范例 中介者模式&#xff08;Mediator Pattern&#xff09;提供了一个中介类&#xff0c;该类通常处理不同类之间的通信&#xff0c;并支持松耦合&#xff0c;使代码易于维护中介者模式是用来降低多个对象和类之间的通信复杂性中介者模式属于行为型模式…

内衣洗衣机怎么选?2024年度最新爆品内衣洗衣机测评

内衣裤洗衣机是一种非常实用的洗衣机&#xff0c;可以有效地保护内衣和贴身衣物的质量和卫生&#xff0c;相比于普通的家用大型洗衣机&#xff0c;内衣裤洗衣机在容量、洗涤方式、控制方式和价格等方面有很大的不同之处&#xff0c;如果您经常需要清洗内衣和贴身衣物&#xff0…

Python实战小项目-骰子模拟器+Turtle绘图

Python实战小项目-骰子模拟器Turtle绘图 骰子模拟器Turtle绘图 骰子模拟器 导入了random模块&#xff0c;该模块提供了生成随机数的功能。 定义了两个变量min_val和max_val&#xff0c;分别表示骰子的最小值和最大值。在这个例子中&#xff0c;骰子的最小值为1&#xff0c;最大…