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

news2024/12/28 18:31:04

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

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 True

this_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 heapq

h = [(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 sys

def 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 md5code

def 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 sha256code

def 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 sha256code

def 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 md5code

def 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 fixture

def test_showhelo(smtp_connection):    assert 0, smtp_connection.helo()

7. 总结

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

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue页面刷新问题:返回之前打开的页面,走了create方法(解决)

vue页面刷新问题:返回之前打开的页面,走了create方法(解决) 直接上图, 我们在开发的时候经常会复制粘贴,导致vue文件的name没有及时修改 我们需要保证name和浏览器的地址一致,这样才能实现缓…

面试题个人总结(面经)

自我介绍 你好,我叫XXX,是今天面试初级蓝队的人员,我毕业于XXXX,专业为网络空间安全,我曾经在XXXXX实习过,有过大概一年左右的工作经验,还有过一定的护网经验,去年在XXX厂商护过网,…

大型网站架构演化总结

本文图解大型网站架构演化。 目录 1、单一应用服务阶段 2、应用与数据服务分离阶段 3、利用缓存提高性能阶段 4、应用服务集群阶段 5、数据库读写分离阶段 6、反向代理与CDN加速阶段 7、分布式数据库阶段 8、 NoSQL与搜索引擎阶段 9、业务拆分阶段 10、分布式服务阶…

电脑要用多少V的电源?电脑电源输入电压是市电

台式电源的输出电压是多少? 电脑电源输出一般有三种不同的电压,分别是: 12V、5V、3.3V。 电脑电源负责给电脑配件供电,如CPU、主板、内存条、硬盘、显卡等,是电脑的重要组成部分。 工作电流根据不同的硬件及其使用状…

从仓储管理看3C电子行业智慧物流的优势

仓储管理是智慧物流的重要组成部分。通过引入自动化、智能化的仓储管理系统,3C电子企业可以实现库存的精准管理、快速分拣和高效配送。这不仅减少了库存成本,还大大提高了运营效率和市场响应速度。 传统的仓储管理依赖于人工操作和纸质文档记录&#xff…

Java项目:37 springboot003图书个性化推荐系统的设计与实现

作者主页:舒克日记 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 springboot003图书个性化推荐系统的设计与实现 管理员:首页、个人中心、学生管理、图书分类管理、图书信息管理、图书预约管理、退换图书…

unity学习(50)——服务器三次注册限制以及数据库化角色信息5--角色信息数据库化收尾

上一节内容结束后确实可以写入文件了,但还有两个问题: 1.一个是players.txt中,每次重启服务器,当注册新账号创建角色时,players.txt之前内容都会清空。 2.players.txt之前已经注册3次的账号,新注册的角色…

做接口测试的流程一般是怎么样的?2024全网最全教程(建议收藏)

在讲接口流程测试之前,首先需要给大家申明下:接口测试对于测试人员而言,非常非常重要,懂功能测试接口测试,就能在企业中拿到一份非常不错的薪资。不信请狠狠点击下方链接(下方链接也有接口学习方法及学习资…

Vue中的v-for中为什么不推荐使用index作为key值

在Vue中,我们经常会用到v-for指令来遍历数组或对象并渲染列表。而在使用v-for指令时,通常会需要给每个遍历的元素指定一个唯一的key值,以帮助Vue更高效地更新DOM。 在很多情况下,我们可能会倾向于使用index作为key值,…

Qt程序设计-柱状温度计自定义控件实例

Qt程序设计-柱状温度计自定义控件实例 本文讲解Qt柱状温度计自定义控件实例。 效果演示 创建温度计类 #ifndef THERMOMETER_H #define THERMOMETER_H#include <QWidget> #include <QPainter> #include <QDebug> #include <QTimer> #include <QPr…

Unity之街机捕鱼

目录 &#x1f62a;炮台系统 &#x1f3b6;炮口方向跟随鼠标 &#x1f3b6;切换炮台 &#x1f62a;战斗系统 &#x1f3ae;概述 &#x1f3ae;单例模式 &#x1f3ae;开炮 &#x1f3ae;子弹脚本 &#x1f3ae;渔网脚本 &#x1f3ae;鱼属性信息的脚本 &#x1f6…

HTML5:七天学会基础动画网页9

在进行接下来的了解之前我们先来看一下3d的xyz轴&#xff0c;下面图中中间的平面就相当于电脑屏幕&#xff0c;z轴上是一个近大远小的效果。 3d转换属性 transform 2D或3D转换 transform-origin 改变旋转点位置 transform-style 嵌套元素在3D空间如何显 …

文件二维码怎么加访问权限?加密、限时、限次的二维码制作技巧

扫码查看或者下载文件已经是现在经常被使用的一种方式&#xff0c;当我们通过这种方式来展现文件内容时&#xff0c;是否能够加入一些权限设置来保障文件的安全性&#xff0c;是很多小伙伴非常关心的一个问题。 想要制作文件二维码&#xff0c;大多情况下会通过在线二维码生成…

GPQA数据集分享

来源: AINLPer公众号&#xff08;每日干货分享&#xff01;&#xff01;&#xff09; 编辑: ShuYini 校稿: ShuYini 时间: 2024-2-28 尽管AI系统在许多任务上表现出色&#xff0c;但在需要大量专业知识和推理能力的任务上仍然存在局限性。为此&#xff0c;纽约大学的研究者提出…

Yii2中如何使用scenario场景,使rules按不同运用进行字段验证

Yii2中如何使用scenario场景&#xff0c;使rules按不同运用进行字段验证 当创建news新闻form表单时&#xff1a; 添加新闻的时候执行create动作。 必填字段&#xff1a;title-标题&#xff0c;picture-图片&#xff0c;description-描述。 这时候在model里News.php下rules规则…

智慧城市中的数据力量:大数据与AI的应用

目录 一、引言 二、大数据与AI技术的融合 三、大数据与AI在智慧城市中的应用 1、智慧交通 2、智慧环保 3、智慧公共安全 4、智慧公共服务 四、大数据与AI在智慧城市中的价值 1、提高城市管理的效率和水平 2、优化城市资源的配置和利用 3、提升市民的生活质量和幸福感…

【人工智能课程】计算机科学博士作业三

【人工智能课程】计算机科学博士作业三 来源&#xff1a;李宏毅2022课程第10课的作业 1 图片攻击概念 图片攻击是指故意对数字图像进行修改&#xff0c;以使机器学习模型产生错误的输出或者产生预期之外的结果。这种攻击是通过将微小的、通常对人类难以察觉的扰动应用于输入…

1.5 简述转置卷积的主要思想以及应用场景

1.5 简述转置卷积的主要思想以及应用场景 普通的卷积主要思想&#xff1a; 普通的卷积操作可以形式化为一个矩阵乘法运算&#xff0c;即yAx&#xff08;1-12&#xff09; 其中&#xff0c;x和y分别是卷积的输入和输出(展平成一维向量形式)&#xff0c;维度分别为d⁽i⁾和d⁽…

计算机设计大赛 深度学习花卉识别 - python 机器视觉 opencv

文章目录 0 前言1 项目背景2 花卉识别的基本原理3 算法实现3.1 预处理3.2 特征提取和选择3.3 分类器设计和决策3.4 卷积神经网络基本原理 4 算法实现4.1 花卉图像数据4.2 模块组成 5 项目执行结果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &a…

[网络安全] PKI

一、PKI 概述 名称; 公钥基础设施 (Public Key Facility) 作用: 通过加密技术和数字签名保证信息安全 组成: 公钥机密技术、数字证书、CA、RA 二、信息安全三要素 机密性&#xff1a;确保仅信息发收双方 能看懂信息 完整性&#xff1a; 确保信息发收完整&#xff0c;不被破坏 …