接口自动化测试分层设计与实践总结
🍅 视频学习:文末有免费的配套视频可观看
🍅 关注公众号:互联网杂货铺,回复1 ,免费获取软件测试全套资料,资料在手,涨薪更快
接口测试三要素:
-
参数构造
-
发起请求,获取响应
-
校验结果
一、原始状态
当我们的用例没有进行分层设计的时候,只能算是一个“苗条式”的脚本。以一个后台创建商品活动的场景为例,大概流程是这样的(默认已经是登录状态下):
创建商品-创建分类-创建优惠券-创建活动
要进行接口测试的话,按照接口测试的三要素来进行,具体的效果如下:
# 1、参数构造createCommodityParams = {"input": {"title": "活动商品","subtitle": "","brand": "","categoryLevel1Code": "12","categoryLevel2Code": "1312","categoryLevel3Code": "131211","detail": [{"uri": "ecommerce/1118d9.jpg","type": 0}],"installInfo": {"installType": 1,"installFee": null},"pictureList": [{"uri": "ecommerce/222.jpg","main": true}],"postageInfo": {"postageType": 2,"postageFee": 1,"postageId": null},"sellerDefinedCode": "","publish": 1,"skuList": [{"skuCode": "","externalSkuCode": "","price": 1,"retailPrice": 6,"stock": 100,"weight": 0,"suggestPrice": 0,"skuAttrValueList": [{"attrCode": "COLOR","attrName": "颜色","attrValue": "绿色","attrValueId": "1001"}]}],"jumpSwitch":false,"recommendCommodityCodeList": [],"recommendFittingCodeList": [],"mallCode": "8h4xxx"}}createCategoryParams = {......}createCouponParams = {......}createPublicityParams = {......}publishCommodityParams = {......}publishPublicityParams = {......}createCommodityParams["input"]["title"] = "autoTest" + str(time.time())createCommodityParams["input"]["mallCode"] = self.mallCodecreateCommodityParams["input"]["skuList"][0]["price"] = random.randint(1,10)createCategoryParams["input"]["categoryName"] = "autoTestCategory" + str(time.time())createCouponParams。。。createPublicityParams。。。publishCommodityParams。。。publishPublicityParams。。。# 2、发起请求,获取响应# 创建商品并获取商品codecreateCommodityRes = api.getUrl("testApi.create.commodity").post.params(createCommodityParams)commodityCode = createCommodityRes["commodityCode"]# 创建分类并获取分类codecreateCategoryRes = api.getUrl("testApi.create.category").post.params(createCategoryParams)categoryCode = createCategoryRes["categoryCode"]# 创建优惠券并获取优惠券codecreateCouponRes = api.getUrl("testApi.create.coupon").post.params(createCouponParams)couponCode = createCouponRes["couponCode"]# 创建活动并关联商品,绑定优惠券,设置分类createPublicityParams["input"]["commodityCode"] = commodityCodecreatePublicityParams["input"]["categoryCode"] = categoryCodecreatePublicityParams["input"]["couponCode"] = couponCodecreatePublicityRes = api.getUrl("testApi.create.publicity").post.params(createPublicityParams)# 结果校验(断言)assert.equal(createPublicityRes["code"], 0)assert.equal(createPublicityRes["publicityName"], createPublicityParams["publicityName"])。。。
按照上面的写法,对于单个脚本的调式来说或许可以,但是一旦用例的数量和复杂程度积累起来后,其维护成本将是巨大的,或者可以说不具备可维护性。
弊端说明:
-
可读性差,所有的处理都放在一起,代码量大,不简洁直观
-
灵活性差,参数写死在脚本,适用用例范围小
-
复用性差,如果其他用例需要同样或类似的步骤,需要重新写一份
-
维护性差,如果接口有任何改动,那么所有涉及到此接口的脚本都需要一一修改
例如:随着用例场景的增加,就可能会出现下面这种情况
按照原始的模式,我们就需要些3个脚本文件分别来描述着3个场景,并且创建商品_API
、创建分类_API
、创建优惠券_API
在场景1,2,3中均出现了;上架商品_API
在场景2,3中均出现。由此我们完全可以预见到,当几百上千的用例场景出现后,这种形式是没有维护性可言的。
二、进化历程
因此我们依照着痛点,以最开始的原始状态为例,对用例进行分层改造,来看看进化后的状态。
1、API 定义层
我们编程的时候会将一些重复的代码进行封装使用,那么这里依然可以借用这种思想,我们将 API 的定义单独抽离,单独定义。
我们期望的效果是这样的:
提前将API的定义放在一层,供用例场景引用,这样当接口有任何修改时,我们只需要修改API definition
层即可。
实例演示
对应着上面的demo,我们就是需要做如下抽离:
class APIDefinition:
‘’’
创建商品API定义
createCommodityParams: 创建商品接口入参
return:创建商品接口响应结果
‘’’
def createCommodityRequest(createCommodityParams):
return api.getUrl(“testApi.create.commodity”).post.params(createCommodityParams)
'''创建分类API定义createCategoryParams: 创建分类接口入参return:创建分类接口响应结果''' def createCategoryRequest(createCategoryParams)return api.getUrl("testApi.create.category").post.params(createCategoryParams)# 创建优惠券接口定义def createCouponRequest(createCouponParams)return api.getUrl("testApi.create.coupon").post.params(createCouponParams)# 创建活动接口定义def createPublicityRequest(createPublicityParams)return api.getUrl("testApi.create.publicity").post.params(createPublicityParams)# ...其余省略
2、Service 层
上面我们已经将接口的定义抽离出来,解决了 API 重复定义的问题,但是再继续分析会发现有一个问题依然没有解决,就是场景的复用性.
再看刚才的图:
3个场景中都有重复的步骤,类似创建商品
、创建分类
、创建优惠
券这些,并且这些步骤都是一个个API的组合,一个步骤对应一个API,在各个步骤之间还会有数据的处理与传递,为了解决这些问题,将对场景再次做抽离,这里我称之为 service
层。
这一层之所以叫做
service(服务)
层,是因为它的作用是用来提供测试用例所需要的各种“服务”,好比参数构建、接口请求、数据处理、测试步骤。
用下图先来看分层的目标:
我们希望将常用的测试场景步骤封装至service
层中,供用例场景调用,增加复用性,也可以理解为测试用例的前置处理;
但是这里还是有一点小问题,就是service
层的东西太多太杂,有些场景步骤可能只适用于我当前的项目用例,在实际的工作中,各个系统间是相互依赖的,前台APP的测试很大可能就依赖后台创建作为前置条件
好比我在APP端只要商品和分类,可能只想创建商品和分类,并不想创建优惠券,这个时候service层就没有适用的场景步骤供调用,那么我就需要根据自己的需要重新封装;可是对于很多单接口的前置数据处理又是一致的,比如:
createCommodityParams["input"]["title"] = "autoTest" + str(time.time())createCommodityParams["input"]["mallCode"] = self.mallCodecreateCommodityParams["input"]["skuList"][0]["price"] = random.randint(1,10)createCategoryParams["input"]["categoryName"] = "autoTestCategory" + str(time.time())createCouponParams。。。createPublicityParams。。。publishCommodityParams。。。publishPublicityParams。。。
重新封装的话还要再处理这一步,就有点麻烦且不符合我们的复用性设计了,因此我们对service
层再细化为3层,分别为:
apiObject:
单接口的预处理层,这一层主要作用是单接口入参的构造,接口的请求与响应值返回
-
每个接口请求不依赖与业务步骤,都是单接口的请求;
-
此外一些简单固定的入参构建也直接放在这里处理,比如随机的商品名,title等,和具体业务流程无关,针对所有调用此接口的场景均适用
caseService:
多接口的预处理层,这一层主要是
测试步骤(teststep)
或场景的有序集合。
-
用例所需要的步骤,通过每一个请求进行组合,每一个步骤都对应着一个API请求,这些步骤会组成一个个场景,各个场景之间可以互相调用组成新的场景,以适应不同的测试用例需求。
-
场景封装好以后可以供不同的测试用例调用,除了当前项目的用例,其他业务线需要的话也可从此
caseService
中选择调用,提高复用性的同时也避免了用例相互依赖的问题。
util:
这一层主要放置针对当前业务的接口需要处理的数据
- 在实际编写测试步骤时,可能部分接口的参数是通过其他接口获取后经过处理才可以使用,或是修改数据格式,或是修改字段名称,亦或是某些 value 的加解密处理等。
细化分层后,各层的职责便更加清晰明确,具体如下图:
实例演示
apiObject:
class ApiObject:def createCommodity(createCommodityParams):inputParams = ApiParamsBuild().createCommodityParamsBuild(createCommodityParams)response = APIDefinition().createCommodityRequest(inputParams)return responsedef createCategory(createCategoryParams):...def createCoupon(createCouponParams):.........class ApiParamsBuild:def createCommodityParamsBuild(createCommodityParams):createCommodityParams["input"]["title"] = "autoTest" + str(time.time())createCommodityParams["input"]["mallCode"] = self.mallCodecreateCommodityParams["input"]["skuList"][0]["price"] = random.randint(1,10)return createCommodityParamsdef createCategoryParamsBuild(createCategoryParams):...def createCouponParamsBuild(createCouponParams):.........
到此,我们来看看原始的用例经过目前封装后的模样:
1、参数构造
createCommodityParams = {"input": {"title": "活动商品","subtitle": "","brand": "","categoryLevel1Code": "12","categoryLevel2Code": "1312","categoryLevel3Code": "131211","detail": [{"uri": "ecommerce/1118d9.jpg","type": 0}],"installInfo": {"installType": 1,"installFee": null},"pictureList": [{"uri": "ecommerce/222.jpg","main": true}],"postageInfo": {"postageType": 2,"postageFee": 1,"postageId": null},"sellerDefinedCode": "","publish": 1,"skuList": [{"skuCode": "","externalSkuCode": "","price": 1,"retailPrice": 6,"stock": 100,"weight": 0,"suggestPrice": 0,"skuAttrValueList": [{"attrCode": "COLOR","attrName": "颜色","attrValue": "绿色","attrValueId": "1001"}]}],"jumpSwitch":false,"recommendCommodityCodeList": [],"recommendFittingCodeList": [],"mallCode": "8h4xxx"}}createCategoryParams = {......}createCouponParams = {......}createPublicityParams = {......}publishCommodityParams = {......}publishPublicityParams = {......}# 2、发起请求,获取响应# 创建商品并获取商品codecreateCommodityRes = ApiObject().createCommodity(createCommodityParams)commodityCode = createCommodityRes["commodityCode"]# 创建分类并获取分类codecreateCategoryRes = ApiObject().createCategory(createCategoryParams)categoryCode = createCategoryRes["categoryCode"]# 创建优惠券并获取优惠券codecreateCouponRes = ApiObject().createCoupon(createCouponParams)couponCode = createCouponRes["couponCode"]# 创建活动并关联商品,绑定优惠券,设置分类createPublicityParams["input"]["commodityCode"] = commodityCodecreatePublicityParams["input"]["categoryCode"] = categoryCodecreatePublicityParams["input"]["couponCode"] = couponCodecreatePublicityRes = ApiObject().createPublicity(createPublicityParams)# 结果校验(断言)assert.equal(createPublicityRes["code"], 0)assert.equal(createPublicityRes["publicityName"], createPublicityParams["publicityName"])。。。
可以看到,现在接口请求的url、method、通用入参处理等已经不会在用例中体现了,接下来继续封装caseService层。caseService:我们将多接口的场景步骤进行封装
``` class CaseService:def createPublicityByCategory(params):# 创建商品并获取商品codecreateCommodityRes = ApiObject().createCommodity(createCommodityParams)commodityCode = createCommodityRes["commodityCode"]# 创建分类并获取分类codecreateCategoryRes = ApiObject().createCategory(createCategoryParams)categoryCode = createCategoryRes["categoryCode"]# 创建优惠券并获取优惠券codecreateCouponRes = ApiObject().createCoupon(createCouponParams)couponCode = createCouponRes["couponCode"]# 创建活动并关联商品,绑定优惠券,设置分类createPublicityParams["input"]["commodityCode"] = commodityCodecreatePublicityParams["input"]["categoryCode"] = categoryCodecreatePublicityParams["input"]["couponCode"] = couponCodecreatePublicityRes = ApiObject().createPublicity(createPublicityParams)return createPublicityRes......
这时体现在用例中的表现就如下层testcase层所示.
3、testcase 层
我们想要的是一个清晰明了,“一劳永逸”的自动化测试用例,就像我们的手工测试用例一样,我们的前置条件可以复用,我们入参可以任意修改,但测试步骤都是固定不变的(前提可能是产品没有偷偷改需求~)。
这一层其实是对应的testsuite(测试用例集),是测试用例的无序集合。其中各个用例之间应该是相互独立,互不干扰,不存在依赖关系,每个用例都可以单独运行。
最终我们期望自动化用例的维护过程中达到的效果如下:
testcase 层:
# 1、参数构造createCommodityParams = {"input": {"title": "活动商品","subtitle": "","brand": "","categoryLevel1Code": "12","categoryLevel2Code": "1312","categoryLevel3Code": "131211","detail": [{"uri": "ecommerce/1118d9.jpg","type": 0}],"installInfo": {"installType": 1,"installFee": null},"pictureList": [{"uri": "ecommerce/222.jpg","main": true}],"postageInfo": {"postageType": 2,"postageFee": 1,"postageId": null},"sellerDefinedCode": "","publish": 1,"skuList": [{"skuCode": "","externalSkuCode": "","price": 1,"retailPrice": 6,"stock": 100,"weight": 0,"suggestPrice": 0,"skuAttrValueList": [{"attrCode": "COLOR","attrName": "颜色","attrValue": "绿色","attrValueId": "1001"}]}],"jumpSwitch":false,"recommendCommodityCodeList": [],"recommendFittingCodeList": [],"mallCode": "8h4xxx"}}createCategoryParams = {......}createCouponParams = {......}createPublicityParams = {......}publishCommodityParams = {......}publishPublicityParams = {......}# 2、发起请求,获取响应createPublicityRes = CaseService().createPublicityByCategory(createCommodityParams,createCategoryParams,createCouponParams...)# 结果校验(断言)assert.equal(createPublicityRes["code"], 0)assert.equal(createPublicityRes["publicityName"], createPublicityParams["publicityName"])。。。
可以看到,这时涉及到用例场景步骤的代码已经非常少了,并且完全独立,与框架、其他用例等均无耦合。
到这里我们再看用例,会发现一点,测试数据依然冗长,那么下面就开始对测试数据进行参数化和数据驱动的处理。
4、testdata
此层用来管理测试数据,作为参数化场景的数据驱动。
参数化: 所谓参数化,简单来说就是将入参利用变量的形式传入,不要将参数写死,增加灵活性,好比搜索商品的接口,不同的关键字和搜索范围作为入参,就会得到不同的搜索结果。上面的例子中其实已经是参数化了。
数据驱动:对于参数,我们可以将其放入一个文件中,可以存放多个入参,形成一个参数列表的形式,然后从中读取参数传入接口即可。常见做数据驱动的有 JSON、CSV、YAML 等。
实例演示
我们以CSV为例,不特别依照某个框架,通常测试框架都具备参数化的功能。
将所需要的入参放入test.csv文件中:
createCommodityParams,createCategoryParams,...{"input": {"title": "活动商品","subtitle": "","brand": "","categoryLevel1Code": "12","categoryLevel2Code": "1312","categoryLevel3Code": "131211","detail": [{"uri": "ecommerce/1118d9.jpg","type": 0}],"installInfo": {"installType": 1,"installFee": null},"pictureList": [{"uri": "ecommerce/222.jpg","main": true}],"postageInfo": {"postageType": 2,"postageFee": 1,"postageId": null},"sellerDefinedCode": "","publish": 1,"skuList": [{"skuCode": "","externalSkuCode": "","price": 1,"retailPrice": 6,"stock": 100,"weight": 0,"suggestPrice": 0,"skuAttrValueList": [{"attrCode": "COLOR","attrName": "颜色","attrValue": "绿色","attrValueId": "1001"}]}],"jumpSwitch":false,"recommendCommodityCodeList": [],"recommendFittingCodeList": [],"mallCode": "8h4xxx"}},...
然后再回到用例层,利用框架参数化的功能对数据进行读取
# 1、参数构造@parametrize(params = readCsv("test.csv"))# 2、发起请求,获取响应createPublicityRes = CaseService().createPublicityByCategory(params)# 结果校验(断言)assert.equal(createPublicityRes["code"], 0)assert.equal(createPublicityRes["publicityName"], createPublicityParams["publicityName"])。。。
注:这里的测试数据,不仅仅局限于接口的请求参数,既然做数据驱动,那么断言也可以维护在此,以减少用例层的代码冗余。
5、rawData
这一层是存放接口原始入参的地方。
某些接口的入参可能很多,其中很多参数值又可能是固定不变的,构建入参的时候我们只想对"变"的值进行动态的维护,而不维护的值就使用原始参数中的默认值,以此减少工作量(emmm…可能也就是CV大法的量吧~)
再者就是数据驱动的数据文件中只维护需要修改的参数,使数据文件更简洁,可阅读性更强。
实例演示:
这种利用原始参数(rawData)的方法我们称之为模板化,实际工作中有多种方式可实现,例如jsonpath、Mustache或者自己根据需求实现方法,本文重点在介绍分层设计,所以就不具体演示模板化技术的细节了,仅说明设计此层的作用。
以实例中的入参createCommodityParams为例,未用模板化技术前,我们要在CSV里面维护完整的入参:
createCommodityParams,createCategoryParams,...{"input": {"title": "活动商品","subtitle": "","brand": "","categoryLevel1Code": "12","categoryLevel2Code": "1312","categoryLevel3Code": "131211","detail": [{"uri": "ecommerce/1118d9.jpg","type": 0}],"installInfo": {"installType": 1,"installFee": null},"pictureList": [{"uri": "ecommerce/222.jpg","main": true}],"postageInfo": {"postageType": 2,"postageFee": 1,"postageId": null},"sellerDefinedCode": "","publish": 1,"skuList": [{"skuCode": "","externalSkuCode": "","price": 1,"retailPrice": 6,"stock": 100,"weight": 0,"suggestPrice": 0,"skuAttrValueList": [{"attrCode": "COLOR","attrName": "颜色","attrValue": "绿色","attrValueId": "1001"}]}],"jumpSwitch":false,"recommendCommodityCodeList": [],"recommendFittingCodeList": [],"mallCode": "8h4xxx"}},...
但是实际上,我们可能仅仅需要修改维护其中某个或某几个字段(例如只想维护商品价格),其余的使用默认值即可,使用模板化技术后可能在CSV中就是这样的表现:
createCommodityParams,createCategoryParams,...{"input": {"skuList": [{"price": 1,"retailPrice": 6}},...
或者这样
- keyPath: $.input.skuList[0].pricevalue: 1- keyPath: $.input.skuList[0].retailPricevalue: 6
亦或使用Mustache,将需要修改的value进行参数化{{value}}。
我们可以看到,这样处理后的数据驱动的文件就变得简洁清晰的许多,当一个文件中维护了多个用例且入参字段很多时,这样维护起来就可以清晰的看出每个数据对应的用例的作用了;
price就是为了测试价格的,stock就是为了测试库存的,publish就是为了测试上下架的等等。
注: 当然,此层的使用视实际情况而定,有可能这个接口的参数本身就没多少,那么直接全量使用就行,或者你就是觉得数据量哪怕再大我都能分得清楚,看的明白,不用也rawData是可以的~
6、Base
此层主要放置我们需要处理的公共前置条件和一些自动化公共方法,也可以理解为公共的config和util。
在我们实际的自动化开发过程中,有很多前置条件或公共方法,比如登录处理,log 处理,断言方法或一些数据处理;
使用过程中所有的service和testcase层都会继承此类,这样这些公共方法和前置条件便可直接通用;在各个业务线之间也可保持一致性。
三、完结
最后,我们来看下整体分层后的目录结构总览:
└─apiautotest└─project└─rawData(原始参数)├─testRawData.json└─service(用例服务)└─apiObject(单接口预处理,单接口入参的构造,接口的请求与响应值返回)├─testApiObject.py└─caseService(多接口预处理,测试步骤(teststep)或场景的有序集合)├─testCaseService.py└─util(工具类)├─util.py└─testcase(测试用例)└─testDataDriven(测试数据驱动)├─testData.csv├─testcase.py(测试用例集)└─testBase.py(测试基类,初始化和公共方法) └─platformapi(Api定义)├─testApiDefinition.py
同时,在这我为大家准备了一份软件测试视频教程(含面试、接口、自动化、性能测试等),就在下方,需要的可以直接去观看,也可以直接【点击文末小卡片免费领取资料文档】
自动化测试从菜鸟到高手【2024最新完整版】,从基础到项目实战(Python自动化测试保姆级教程)
相关文章:
接口自动化测试分层设计与实践总结
🍅 视频学习:文末有免费的配套视频可观看 🍅 关注公众号:互联网杂货铺,回复1 ,免费获取软件测试全套资料,资料在手,涨薪更快 接口测试三要素: 参数构造 发起请求&#x…...

集合(下)Map集合的使用
文章目录 前言一、Map接口二、Map接口的实现类 1.HashMap类2.TreeMap类总结 前言 Map集合没有继承Collection接口,不能像List集合和Set集合那样直接使用Collection接口的方法。Map集合其自身通过以key到value的映射关系实现的集合,也有相应的许多方法。类…...
AAPT: error: resource android:attr/dialogCornerRadius not found.
ERROR:D:\android.gradle\caches\transforms-3\b3b98118f65da38d0ad9da84cfc70a72\transformed\appcompat-1.0.0\res\values-v28\values-v28.xml:5:5-8:13: AAPT: error: resource android:attr/dialogCornerRadius not found. 请帮我看看这个错误是什么意思。我改如何做。 这个…...

数字功放VS模拟功放,选择适合你的音频解决方案
数字功放和模拟功放是音频系统中常用的两种功放技术,适用于不同的音频应用,都具有各自的优势和特点。本文将为您详细介绍数字功放和模拟功放的差异,并帮助您找到适合自己的音频解决方案。 1、数字功放是一种利用数字信号处理技术的功放。它将…...
5.88 BCC工具之tcpsynbl.py解读
一,工具简介 tcpsynbl工具以直方图的形式显示SYN到达时的TCP SYN积压大小。这可以让我们了解应用程序距离达到积压限制并丢弃SYN(导致SYN重传产生性能问题)还有多远。 TCP SYN 数据包则通常用于启动 TCP 三次握手过程的第一次握手。 二,代码示例 #!/usr/bin/env python…...

GVRP实现vlan的自动创建和注册
拓扑图 资源已上传 流程 第一、每台交换机的全局及端口下使能GVRP功能 第二、配置交换机的二层连通性,交换机某些端口配置Trunk端口,并允许相应的vlan通过 第三、在交换机4和5配置静态vlan100,然后查看1和3交换机是否有vlan100的定义&…...

Oracle VM VirtualBox修改磁盘大小
一、 修改虚拟机磁盘大小 先把虚拟机停掉。再增加磁盘大小。 路径中有空格的用""包起来。 D:\Program Files\Oracle\VirtualBox>VBoxManage.exe modifyhd "D:\Program Files\VirtualBox VMs\mycentos\mycentos.vdi" --resize 30000 0%...10%...20%...3…...

【嵌入式硬件】步进电机
1.步进电机简介 1.1步进电机基本原理 步进电机的英文是stepping motor。step的中文意思是行走、迈步。所以仅从字面上我们就可以得知,步进电机就是一步一步移动的电动机。说的官方一点儿,步进电机是一种将电脉冲信号转换成相应角位移或者线位移的电动机(直线电机)。下图为…...

FlyControls 是 THREE.js 中用于实现飞行控制的类,它用于控制摄像机在三维空间中的飞行。
demo演示地址 FlyControls 是 THREE.js 中用于实现飞行控制的类,它用于控制摄像机在三维空间中的飞行。 入参: object:摄像机对象,即要控制的摄像机。domElement:用于接收用户输入事件的 HTML 元素,通常…...

【Java程序设计】【C00366】基于(JavaWeb)Springboot的纹理生产图片系统(有论文)
TOC 博主介绍:java高级开发,从事互联网行业六年,已经做了六年的毕业设计程序开发,开发过上千套毕业设计程序,博客中有上百套程序可供参考,欢迎共同交流学习。 项目简介 项目获取 🍅文末点击卡片…...
编译原理Lab. 1 初代编译器实验说明和要求
目录 Lab. 1 初代编译器实验说明和要求一、初代编译器功能描述二、初代编译器文法要求三、初代编译器测试样例四、初代编译器提交要求五、初代编译器实验测试框架说明 代码与思路 Lab. 1 初代编译器实验说明和要求 一、初代编译器功能描述 初代编译器将 C 语言顺序语句序列翻…...
python判断工作日,节假日
一、概述 需要判断一个日期是否为工作日,节假日。 找到一个现成的插件,蛮好用的。 插件介绍 https://pypi.org/project/chinesecalendar/ 判断某年某月某一天是不是工作日/节假日。 支持 2004年 至 2020年,包括 2020年 的春节延长。 兼容…...

练习4-权重衰减(李沐函数简要解析)
环境:练习1的环境 代码详解 0.导入库 import torch from torch import nn from d2l import torch as d2l1.初始化数据 这里初始化出train_iter test_iter 可以查一下之前的获取Fashion数据集后的数据格式与此对应 n_train, n_test, num_inputs, batch_size 20, 100, 200, …...
websocket 中 request-line 中的URI编码问题
首先,request-line组成如下: Request-Line Method SP Request-URI SP HTTP-Version CRLF 在 rfc6455 规范的 5.1.2 Request-URI 中,有这样的描述: The Request-URI is transmitted in the format specified in section 3.2.1. …...

为何ChatGPT日耗电超50万度?
看新闻说,ChatGPT每天的耗电量是50万度,国内每个家庭日均的耗电量不到10度,ChatGPT耗电相当于国内5万个家庭用量。 网上流传,英伟达创始人黄仁勋说:“AI的尽头是光伏和储能”,大佬的眼光就是毒辣ÿ…...
__init__.py 的作用
在 Python 中,包含一个名为 __ init __.py 的文件的目录被称为一个包(package)。 __ init __.py 文件的作用有以下几点: 指示包含该文件的目录是一个 Python 包:当 Python 导入一个包时,会查找该包所在目录…...

Redis到底是多线程还是单线程?
Redis6.0之前:是单线程模式。 Redis6.0之后:Redis的IO线程是多线程,worker线程是单线程。 Redis6.0之前:单线程 Redis6.0之后:Redis的IO线程是多线程,worker线程是单线程。...
JAVA 100道题(18)
18.实现一个除法运算的方法,能够处理被除数为零的情况,并抛出异常。 在Java中,你可以创建一个除法运算的方法,该方法接受两个整数作为参数,分别代表被除数和除数。如果被除数为零,你可以抛出一个自定义的异…...
【C++】每日一题 137 只出现一次的数字
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。 #include <vector>int singleNumber(std::vecto…...

RAG进阶笔记:RAG进阶
1 查询/索引部分 1.1 层次索引 创建两个索引——一个由摘要组成,另一个由文档块组成分两步进行搜索:首先通过摘要过滤出相关文档,接着只在这个相关群体内进行搜索 1.2 假设性问题 让LLM为每个块生成一个假设性问题,并将这些问…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...