Python + requests实现接口自动化框架

🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快

为什么要做接口自动化框架

1、业务与配置的分离

2、数据与程序的分离;数据的变更不影响程序

3、有日志功能,实现无人值守

4、自动发送测试报告

5、不懂编程的测试人员也可以进行测试

正常接口测试的流程是什么?

确定接口测试使用的工具----->配置需要的接口参数----->进行测试----->检查测试结果----->生成测试报告

测试的工具:python+requests

接口测试用例:excel

一、接口框架如下

1、action包:用来存放关键字函数

2、config包:用来存放配置文件

3、TestData:用来存放测试数据,excel表

4、Log包:用来存放日志文件

5、utils包:用来存放公共的类

6、运行主程序interface_auto_test.py

7、Readme.txt:告诉团队组员使用改框架需要注意的地方

二、接口的数据规范设计---Case设计

一个sheet对应数据库里面一张表

APIsheet存放
编号;从1开始
接口的名称(APIName);
请求的url(RequestUrl);
请求的方法(RequestMethod);
传参的方式(paramsType):post/get请求方法不一样
用例说明(APITestCase)
是否执行(Active)部分接口已测通,下次不用测试,直接把这里设置成N,跳过此接口

post与get的区别

查看post详情

post请求参数一般是json串,参数放在from表单里面;参数一般不可见,相对来说安全性高些

查看get详情

get请求参数一般直接放在url里面

2.1注册接口用例

  • RequestData:请求的数据
  • (开发制定的传参方式)
  • RelyData:数据依赖
  • ResponseCode:响应code
  • ResponseData:响应数据
  • DataStore:存储的依赖数据;如果存在数据库里面,在表里增加一个字段用来存依赖的数据
  • (存储的方式是编写接口自动化的人员来设定的存储方式)
  • CheckPoint:检查点
  • Active:是否执行
  • Status:执行用例的状态,方便查看用例是否执行成功
  • ErrorInfo:case运行失败,失败的错误信息;eg:是也本身的原因还是case设置失败,还是其他原因

2.2登录接口用例

RequestData:请求的数据
(开发制定的传参方式)

RelyData:数据依赖
(存储的方式是编写接口自动化的人员来设定的存储方式)

ResponseCode:响应code

ResponseData:响应数据

DataStore:存储的依赖数据;如果存在数据库里面,在表里增加一个字段用来存依赖的数据
(存储的方式是编写接口自动化的人员来设定的存储方式)

CheckPoint:检查点

Active:是否执行

Status:执行用例的状态,方便查看用例是否执行成功

ErrorInfo:case运行失败,失败的错误信息;eg:是也本身的原因还是case设置失败,还是其他原因

重点说明下RelyData:数据依赖

采取的是字典:key:value来存储数据格式;
{"request":{"username":"register->1","password":"register->1"},"response":{"code":"register->1"}}

格式化之后:

{"request":{"username":"register->1","password":"register->1"},"response":{"code":"register->1"}
}

三、创建utils包:用来存放公共的类

3.1 ParseExcel.py 操作封装excel的类(ParseExcel.py)

#encoding=utf-8
import openpyxl
from openpyxl.styles import Border, Side, Font
import timeclass ParseExcel(object):def __init__(self):self.workbook = Noneself.excelFile = Noneself.font = Font(color = None) # 设置字体的颜色# 颜色对应的RGB值self.RGBDict = {'red': 'FFFF3030', 'green': 'FF008B00'}def loadWorkBook(self, excelPathAndName):# 将excel文件加载到内存,并获取其workbook对象try:self.workbook = openpyxl.load_workbook(excelPathAndName)except Exception as err:raise errself.excelFile = excelPathAndNamereturn self.workbookdef getSheetByName(self, sheetName):# 根据sheet名获取该sheet对象try:# sheet = self.workbook.get_sheet_by_name(sheetName)sheet = self.workbook[sheetName]return sheetexcept Exception as err:raise errdef getSheetByIndex(self, sheetIndex):# 根据sheet的索引号获取该sheet对象try:# sheetname = self.workbook.get_sheet_names()[sheetIndex]sheetname = self.workbook.sheetnames[sheetIndex]except Exception as err:raise err# sheet = self.workbook.get_sheet_by_name(sheetname)sheet = self.workbook[sheetname]return sheetdef getRowsNumber(self, sheet):# 获取sheet中有数据区域的结束行号return sheet.max_rowdef getColsNumber(self, sheet):# 获取sheet中有数据区域的结束列号return sheet.max_columndef getStartRowNumber(self, sheet):# 获取sheet中有数据区域的开始的行号return sheet.min_rowdef getStartColNumber(self, sheet):# 获取sheet中有数据区域的开始的列号return sheet.min_columndef getRow(self, sheet, rowNo):# 获取sheet中某一行,返回的是这一行所有的数据内容组成的tuple,# 下标从1开始,sheet.rows[1]表示第一行try:rows = []for row in sheet.iter_rows():rows.append(row)return rows[rowNo - 1]except Exception as err:raise errdef getColumn(self, sheet, colNo):# 获取sheet中某一列,返回的是这一列所有的数据内容组成tuple,# 下标从1开始,sheet.columns[1]表示第一列try:cols = []for col in sheet.iter_cols():cols.append(col)return cols[colNo - 1]except Exception as err:raise errdef getCellOfValue(self, sheet, coordinate = None,rowNo = None, colsNo = None):# 根据单元格所在的位置索引获取该单元格中的值,下标从1开始,# sheet.cell(row = 1, column = 1).value,# 表示excel中第一行第一列的值if coordinate != None:try:return sheet[coordinate]except Exception as err:raise errelif coordinate is None and rowNo is not None and \colsNo is not None:try:return sheet.cell(row = rowNo, column = colsNo).valueexcept Exception as err:raise errelse:raise Exception("Insufficient Coordinates of cell !")def getCellOfObject(self, sheet, coordinate = None,rowNo = None, colsNo = None):# 获取某个单元格的对象,可以根据单元格所在位置的数字索引,# 也可以直接根据excel中单元格的编码及坐标# 如getCellObject(sheet, coordinate = 'A1') or# getCellObject(sheet, rowNo = 1, colsNo = 2)if coordinate != None:try:# return sheet.cell(coordinate = coordinate)return sheet[coordinate]except Exception as err:raise errelif coordinate == None and rowNo is not None and \colsNo is not None:try:return sheet.cell(row = rowNo,column = colsNo)except Exception as err:raise errelse:raise Exception("Insufficient Coordinates of cell !")def writeCell(self, sheet, content, coordinate = None,rowNo = None, colsNo = None, style = None):#根据单元格在excel中的编码坐标或者数字索引坐标向单元格中写入数据,# 下标从1开始,参style表示字体的颜色的名字,比如red,greenif coordinate is not None:try:# sheet.cell(coordinate = coordinate).value = contentsheet[coordinate] = contentif style is not None:sheet[coordinate].\font = Font(color = self.RGBDict[style])self.workbook.save(self.excelFile)except Exception as e:raise eelif coordinate == None and rowNo is not None and \colsNo is not None:try:sheet.cell(row = rowNo,column = colsNo).value = contentif style:sheet.cell(row = rowNo,column = colsNo).\font = Font(color = self.RGBDict[style])self.workbook.save(self.excelFile)except Exception as e:raise eelse:raise Exception("Insufficient Coordinates of cell !")def writeCellCurrentTime(self, sheet, coordinate = None,rowNo = None, colsNo = None):# 写入当前的时间,下标从1开始now = int(time.time())  #显示为时间戳timeArray = time.localtime(now)currentTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)if coordinate is not None:try:sheet.cell(coordinate = coordinate).value = currentTimeself.workbook.save(self.excelFile)except Exception as e:raise eelif coordinate == None and rowNo is not None \and colsNo is not None:try:sheet.cell(row = rowNo, column = colsNo).value = currentTimeself.workbook.save(self.excelFile)except Exception as e:raise eelse:raise Exception("Insufficient Coordinates of cell !")if __name__ == '__main__':# 测试代码pe = ParseExcel()pe.loadWorkBook(r'D:\ProgramSourceCode\Python Source Code\WorkSpace\InterfaceFrame2018\inter_test_data.xlsx')sheetObj = pe.getSheetByName(u"API")print("通过名称获取sheet对象的名字:", sheetObj.title)# print help(sheetObj.rows)print("通过index序号获取sheet对象的名字:", pe.getSheetByIndex(0).title)sheet = pe.getSheetByIndex(0)print(type(sheet))print(pe.getRowsNumber(sheet))  #获取最大行号print(pe.getColsNumber(sheet))  #获取最大列号rows = pe.getRow(sheet, 1)  #获取第一行for i in rows:print(i.value)# # 获取第一行第一列单元格内容# print pe.getCellOfValue(sheet, rowNo = 1, colsNo = 1)# pe.writeCell(sheet, u'我爱祖国', rowNo = 10, colsNo = 10)# pe.writeCellCurrentTime(sheet, rowNo = 10, colsNo = 11)

3.2 封装get/post请求(HttpClient.py)

import requests
import jsonclass HttpClient(object):def __init__(self):passdef request(self, requestMethod, requestUrl, paramsType,requestData, headers =None, **kwargs):if requestMethod == "post":print("---", requestData, type(requestData))if paramsType == "form":response = self.__post(url = requestUrl, data = json.dumps(eval(requestData)),headers = headers, **kwargs)return responseelif paramsType == "json":response = self.__post(url = requestUrl, json = json.dumps(eval(requestData)),headers = headers, **kwargs)return responseelif requestMethod == "get":request_url = requestUrlif paramsType == "url":request_url = "%s%s" %(requestUrl, requestData)response = self.__get(url = request_url, params = requestData, **kwargs)return responsedef __post(self, url, data = None, json = None, headers=None,**kwargs):print("----")response = requests.post(url=url, data = data, json=json, headers=headers)return responsedef __get(self, url, params = None, **kwargs):response = requests.get(url, params = params, **kwargs)return responseif __name__ == "__main__":hc = HttpClient()res = hc.request("get", "http://39.106.41.11:8080/getBlogContent/", "url",'2')print(res.json())

 3.3 封装MD5(md5_encrypt)

import hashlibdef md5_encrypt(text):m5 = hashlib.md5()m5.update(text.encode("utf-8"))value = m5.hexdigest()return valueif __name__ == "__main__":print(md5_encrypt("sfwe"))

3.4 封装Log

import logging
import logging.config
from config.public_data import baseDir# 读取日志配置文件
logging.config.fileConfig(baseDir + "\config\Logger.conf")
# 选择一个日志格式
logger = logging.getLogger("example02")#或者example01def debug(message):# 定义dubug级别日志打印方法logger.debug(message)def info(message):# 定义info级别日志打印方法logger.info(message)def warning(message):# 定义warning级别日志打印方法logger.warning(message)

3.5 封装发送Email类

​import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from ProjVar.var import *import os
import smtplib
from email import encoders
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from email.utils import formataddrdef send_mail():mail_host="smtp.qq.com"  #设置服务器mail_user="xiangxiang"    #用户名mail_pass="cmxx"   #口令sender = 'cm2019@126.com'receivers = ['672014873@qq.com',"cm2019@126.com"] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱# 创建一个带附件的实例message = MIMEMultipart()message['From'] = formataddr(["自动化测试", "cm2019@126.com"])message['To'] = ','.join(receivers)subject = '自动化测试执行报告'message['Subject'] = Header(subject, 'utf-8')message["Accept-Language"]="zh-CN"message["Accept-Charset"]="ISO-8859-1,utf-8,gbk"# 邮件正文内容message.attach(MIMEText('最新执行的自动化测试报告,请参阅附件内容!', 'plain', 'utf-8'))# 构造附件1,传送测试结果的excel文件att = MIMEBase('application', 'octet-stream')att.set_payload(open(ProjDirPath+"\\testdata\\testdata.xlsx", 'rb').read())att.add_header('Content-Disposition', 'attachment', filename=('gbk', '', "自动化测试报告.xlsx"))encoders.encode_base64(att)message.attach(att)"""# 构造附件2,传送当前目录下的 runoob.txt 文件att2 = MIMEText(open('e:\\a.py','rb').read(), 'base64', 'utf-8')att2["Content-Type"] = 'application/octet-stream'att2["Content-Disposition"] = 'attachment; filename="a.py"'message.attach(att2)"""try:smtpObj = smtplib.SMTP(mail_host)smtpObj.login(mail_user, mail_pass)smtpObj.sendmail(sender, receivers, message.as_string())print("邮件发送成功")except smtplib.SMTPException as e:print("Error: 无法发送邮件", e)if __name__ == "__main__":send_mail()
​

四、 创建config包 用来存放公共的参数、配置文件、长时间不变的变量值

创建public_data.p

import os
# 整个项目的根目录绝对路劲
baseDir = os.path.dirname(os.path.dirname(__file__))# 获取测试数据文件的绝对路径
file_path = baseDir + "/TestData/inter_test_data.xlsx"API_apiName = 2
API_requestUrl = 3
API_requestMothod = 4
API_paramsType = 5
API_apiTestCaseFileName = 6
API_active = 7CASE_requestData = 1
CASE_relyData = 2
CASE_responseCode = 3
CASE_responseData = 4
CASE_dataStore = 5
CASE_checkPoint = 6
CASE_active = 7
CASE_status = 8
CASE_errorInfo = 9# 存储请求参数里面依赖的数据
REQUEST_DATA = {}# 存储响应对象中的依赖数据
RESPONSE_DATA = {}if __name__=="__main__":print(file_path)print(baseDir)

五、创建TestData目录,用来存放测试文件

inter_test_data.xlsx

六、创建action包,用来存放关键字函数

6.1 解决数据依赖 (GetRely.py)

from config.public_data import REQUEST_DATA, RESPONSE_DATA
from utils.md5_encrypt import md5_encryptREQUEST_DATA = {"用户注册":{"1":{"username":"zhangsan", "password":"dfsdf23"},"headers":{"cookie":"asdfwerw"}}}
RESPONSE_DATA = {"用户注册":{"1":{"code":"00"}, "headers":{"age":2342}}}class GetRely(object):def __init__(self):pass@classmethoddef get(self, dataSource, relyData, headSource = {}):print(type(dataSource))print(dataSource)data = dataSource.copy()for key, value in relyData.items():if key == "request":#说明应该去REQUEST_DATA中获取for k, v in value.items():interfaceName, case_idx = v.split("->")val = REQUEST_DATA[interfaceName][case_idx][k]if k == "password":data[k] = md5_encrypt(val)else:data[k] = valelif key == "response":# 应该去RESPONSE_DATA中获取for k, v in value.items():interfaceName, case_idx = v.split("->")data[k] = RESPONSE_DATA[interfaceName][case_idx][k]elif key == "headers":if headSource:for key, value in value.items():if key == "request":for k, v in value.items():for i in v:headSource[i] = REQUEST_DATA[k]["headers"][i]elif key == "response":for i, val in value.items():for j in val:headSource[j] = RESPONSE_DATA[i]["headers"][j]return "%s" %dataif __name__ == "__main__":s = {"username": "", "password": "","code":""}h = {"cookie":"123", "age":332}rely = {"request": {"username": "用户注册->1", "password": "用户注册->1"},"response":{"code":"用户注册->1"},"headers":{"request":{"用户注册":["cookie"]},"response":{"用户注册":["age"]}}}print(GetRely.get(s, rely, h))

6.2 解决数据存储(RelyDataStore.y)

from config.public_data import RESPONSE_DATA, REQUEST_DATAclass RelyDataStore(object):def __init__(self):pass@classmethoddef do(cls, storePoint, apiName, caseId, request_source = {}, response_source = {}, req_headers={}, res_headers = {}):for key, value in storePoint.items():if key == "request":# 说明需要存储的依赖数据来自请求参数,应该将数据存储到REQUEST_DATAfor i in value:if i in request_source:val = request_source[i]if apiName not in REQUEST_DATA:# 说明存储数据的结构还未生成,需要指明数据存储结构REQUEST_DATA[apiName]={str(caseId): {i: val}}else:#说明存储数据结构中最外层结构已存在if str(caseId) in REQUEST_DATA[apiName]:REQUEST_DATA[apiName][str(caseId)][i] = valelse:# 说明内层结构不完整,需要指明完整的结构REQUEST_DATA[apiName][str(caseId)] = {i: val}else:print("请求参数中不存在字段" + i)elif key == "response":#说明需要存储的依赖数据来自接口的响应body,应该将数据存储到RESPONSE_DATAfor j in value:if j in response_source:val = response_source[j]if apiName not in RESPONSE_DATA:# 说明存储数据的结构还未生成,需要指明数据存储结构RESPONSE_DATA[apiName]={str(caseId): {j: val}}else:#说明存储数据结构中最外层结构已存在if str(caseId) in RESPONSE_DATA[apiName]:RESPONSE_DATA[apiName][str(caseId)][j] = valelse:# 说明内层结构不完整,需要指明完整的结构RESPONSE_DATA[apiName][str(caseId)] = {j: val}else:print("接口的响应body中不存在字段" + j)elif key == "headers":for k, v in value.items():if k == "request":# 说明需要往REQUEST_DATA变量中写入存储数据for item in v:if item in req_headers:header = req_headers[item]if "headers" in REQUEST_DATA[apiName]:REQUEST_DATA[apiName]["headers"][item] = headerelse:REQUEST_DATA[apiName]["headers"] = {item: header}elif k == "response":# 说明需要往RESPONSE_DATA变量中写入存储数据for it in v:if it in res_headers:header = res_headers[it]if "headers" in RESPONSE_DATA[apiName]:RESPONSE_DATA[apiName]["headers"][it] = headerelse:RESPONSE_DATA[apiName]["headers"] = {item: header}print(REQUEST_DATA)print(RESPONSE_DATA)if __name__ == "__main__":r = {"username": "srwcx01", "password": "wcx123wac1", "email": "wcx@qq.com"}req_h = {"cookie":"csdfw23"}res_h = {"age":597232}s = {"request": ["username", "password"], "response": ["userid"],"headers":{"request":["cookie"],"response":["age"]}}res = {"userid": 12, "code": "00"}RelyDataStore.do(s, "register", 1, r, res, req_headers=req_h, res_headers=res_h)print(REQUEST_DATA)print(RESPONSE_DATA)

6.3 校验数据结果(CheckResult.py)

import reclass CheckResult(object):def __init__(self):pass@classmethoddef check(self, responseObj, checkPoint):responseBody = responseObj.json()# responseBody = {"code": "", "userid": 12, "id": "12"}errorKey = {}for key, value in checkPoint.items():if key in responseBody:if isinstance(value, (str, int)):# 等值校验if responseBody[key] != value:errorKey[key] = responseBody[key]elif isinstance(value, dict):sourceData = responseBody[key]if "value" in value:# 模糊匹配校验regStr = value["value"]rg = re.match(regStr, "%s" %sourceData)if not rg:errorKey[key] = sourceDataelif "type" in value:# 数据类型校验typeS = value["type"]if typeS == "N":# 说明是整形校验if not isinstance(sourceData, int):errorKey[key] = sourceDataelse:errorKey[key] = "[%s] not exist" %keyreturn errorKeyif __name__ == "__main__":r = {"code": "00", "userid": 12, "id": 12}c = {"code": "00", "userid": {"type": "N"}, "id": {"value": "\d+"}}print(CheckResult.check(r, c))

6.4 往excel里面写结果

from config.public_data import *def write_result(wbObj, sheetObj, responseData, errorKey, rowNum):try:# 写响应bodywbObj.writeCell(sheetObj, content="%s" %responseData,rowNo = rowNum, colsNo=CASE_responseData)# 写校验结果状态及错误信息if errorKey:wbObj.writeCell(sheetObj, content="%s" %errorKey,rowNo=rowNum, colsNo=CASE_errorInfo)wbObj.writeCell(sheetObj, content="faild",rowNo=rowNum, colsNo=CASE_status, style="red")else:wbObj.writeCell(sheetObj, content="pass",rowNo=rowNum, colsNo=CASE_status, style="green")except Exception as err:raise err

七、创建Log目录用来存放日志

八、主函数

#encoding=utf-8
import requests
import json
from action.get_rely import GetRely
from config.public_data import *
from utils.ParseExcel import ParseExcel
from utils.HttpClient import HttpClient
from action.data_store import RelyDataStore
from action.check_result import CheckResult
from action.write_result import write_result
from utils.Log import *def main():parseE = ParseExcel()parseE.loadWorkBook(file_path)sheetObj = parseE.getSheetByName("API")activeList = parseE.getColumn(sheetObj, API_active)for idx, cell in enumerate(activeList[1:], 2):if cell.value == "y":#需要被执行RowObj = parseE.getRow(sheetObj, idx)apiName = RowObj[API_apiName -1].valuerequestUrl = RowObj[API_requestUrl - 1].valuerequestMethod = RowObj[API_requestMothod - 1].valueparamsType = RowObj[API_paramsType - 1].valueapiTestCaseFileName = RowObj[API_apiTestCaseFileName - 1].value# 下一步读取用例sheet表,准备执行测试用例caseSheetObj = parseE.getSheetByName(apiTestCaseFileName)caseActiveObj = parseE.getColumn(caseSheetObj, CASE_active)for c_idx, col in enumerate(caseActiveObj[1:], 2):if col.value == "y":#需要执行的用例caseRowObj = parseE.getRow(caseSheetObj, c_idx)requestData = caseRowObj[CASE_requestData - 1].valuerelyData = caseRowObj[CASE_relyData - 1].valueresponseCode = caseRowObj[CASE_responseCode - 1].valueresponseData = caseRowObj[CASE_responseData - 1].valuedataStore = caseRowObj[CASE_dataStore -1].valuecheckPoint = caseRowObj[CASE_checkPoint - 1].value#发送接口请求之前需要做一下数据依赖的处理if relyData:logging.info("处理第%s个接口的第%s条用例的数据依赖!")requestData = GetRely.get(eval(requestData), eval(relyData))httpC = HttpClient()response = httpC.request(requestMethod=requestMethod,requestData=requestData,requestUrl=requestUrl,paramsType=paramsType)# 获取到响应结果后,接下来进行数据依赖存储逻辑实现if response.status_code == 200:responseData = response.json()# 进行依赖数据存储if dataStore:RelyDataStore.do(eval(dataStore), apiName, c_idx - 1, eval(requestData), responseData)# 接下来就是校验结果else:logging.info("接口【%s】的第【%s】条用例,不需要进行依赖数据存储!" %(apiName, c_idx))if checkPoint:errorKey = CheckResult.check(response, eval(checkPoint))write_result(parseE, caseSheetObj, responseData, errorKey, c_idx)else:logging.info("接口【%s】的第【%s】条用例,执行失败,接口协议code非200!" %(apiName, c_idx))else:logging.info("第%s个接口的第%s条用例,被忽略执行!" %(idx -1, c_idx-1))else:logging.info("第%s行的接口被忽略执行!" %(idx -1))if __name__=="__main__":main()

框架待完善,请大家多多指教~

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。

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

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

相关文章

Linux——基本指令

我们今天学习Linux最基础的指令 ls 指令 语法: ls [选项] [⽬录或⽂件] 功能:对于⽬录,该命令列出该⽬录下的所有⼦⽬录与⽂件。对于⽂件,将列出⽂件名以及其他信 息。 命令中的选项,一次可以传递多个 &#xff0c…

【Godot4.3】自定义简易菜单栏节点ETDMenuBar

概述 Godot中的菜单创建是一个复杂的灾难性工作,往往无从下手,我也是不止一次尝试简化菜单的创建。 从自己去年的发明“简易树形数据”用于简化Tree控件获得灵感,于是尝试编写了用于表示菜单数据的EasyMenuData类,以及对应的纯文…

esp32串口通信

1、查看esp32的引脚图,寻找对应的串口 根据原理图,芯片上有3个串口(UART0, UART1和UART2),但是UART1没有引出引脚。其中UART0(GPIO3用于U0RXD,GPIO1用于U0TXD)用作下载、调试串口,引脚不可改变&…

内部静态类和非内部静态类的区别

目录 问题: 原理: 外部类与非内部静态类 外部类与静态内部类 加载顺序 总结: 1.非静态内部类依赖于外部类的实例,而静态内部类不依赖于外部类的实例。 2.非静态内部类可以访问外部类的实例变量和方法,而静态内部…

Redis分布式锁的实现(Redission)

写在前面 本人在学习Redis过程中学习到分布式锁时太多困惑和疑难杂点 需要总结梳理思路 以下思路都是最简单最基本的思路 主要用到Redission工具类 会涉及到看门狗机制等 本文内容部分引自Javaguide,小林coding等热门八股 用于个人学习用途 分布式锁介绍 对于单机多线程来说…

【愚公系列】《Python网络爬虫从入门到精通》038-SQLite数据库

标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。近期荣誉2022年度…

【开源免费】基于SpringBoot+Vue.JS网络海鲜市场系统(JAVA毕业设计)

本文项目编号 T 222 ,文末自助获取源码 \color{red}{T222,文末自助获取源码} T222,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

JDK17安装方法/如何安装JDK17/环境变量配置

双击安装 我们这里安装jdk17 然后更改jdk目录 jdk环境变量配置 右键——>此电脑——>高级系统设置——>环境变量 当前用户的环境变量,另外有一个系统变量。 一般我们配置系统环境变量 一、配置JAVA_HOME 这里我们配置一个JAVA_HOME 我这里先前的jdk…

python集合set的常用方法

目录 集合的定义 集合的基础操作 多个集合之间的操作 集合的for循环 集合的定义 集合的基础操作 集合.add(元素) 添加新元素 集合.pop() 从集合中随机取出一个元素 集合.clear() 清空集合 集合.remove(元素) 移除元素 #定义集合,集合自动去重了 set1{"春"…

看得见摸得着的AI:具身智能

“如果Siri有手有脚,你的生活会变成什么样?” 想象一下: • 你家的扫地机器人不再横冲直撞,而是像猫咪一样轻巧绕过桌脚 • 手机里的语音助手能“摸”到你发烧的额头,主动帮你叫医生 • 工厂里的机械臂会边干活边学习&…

ES、OAS、ERP、电子政务、企业信息化(高软35)

系列文章目录 ES、OAS、ERP、电子政务、企业信息化 文章目录 系列文章目录前言一、专家系统(ES)二、办公自动化系统(OAS)三、企业资源规划(ERP)四、典型信息系统架构模型1.政府信息化和电子政务2.企业信息…

【算法学习之路】4.简单数论(4)

简单数论(4) 前言三.高精度1.什么是高精度2.解决办法 精度乘除法一.精度乘法1.数据的存储2.步骤3.例题:高精度乘法 二.精度除法1.例子2.步骤3.例题:高精度除法 前言 我会将一些常用的算法以及对应的题单给写完,形成一套…

Linux 动静态库和_make_进度条(一)

文章目录 一、如何理解条件编译二、动静态库1. 理论2. 实践3. 解决普通用户的sudo问题4. 技术上理解库 三、make和make_file 一、如何理解条件编译 1. gcc code.c -o code -DM 命令行级别的宏定义预处理的本质就是修改编辑我们的文本代码 头文件展开到源文件中去注释宏替换条…

基于springboot+vue的拖恒ERP-物资管理

开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:…

Leetcode-853. Car Fleet [C++][Java]

目录 一、题目描述 二、解题思路 Leetcode-853. Car Fleethttps://leetcode.com/problems/car-fleet/description/ 一、题目描述 There are n cars at given miles away from the starting mile 0, traveling to reach the mile target. You are given two integer array …

Vue核心知识:动态路由实现完整方案

在Vue中实现动态路由,并结合后端接口和数据库表设计,是一个复杂的项目,需要多个技术栈和步骤的配合。以下将详细描述整个实现过程,包括数据库设计、后端接口设计、前端路由配置以及如何实现动态路由的功能。 目录 一、需求分析二…

CMU15445(2023fall) Project #4 - Concurrency Control踩坑历程

把树木磨成月亮最亮时的样子, 就能让它更快地滚下山坡, 有时会比骑马还快。 完整代码见: SnowLegend-star/CMU15445-2023fall: Having Conquered the Loftiest Peak, We Stand But a Step Away from Victory in This Stage. With unwavering…

天疱疮是一种慢性、严重的皮肤疾病

天疱疮是一种慢性、严重的皮肤疾病,对患者的日常生活带来很大的困扰。为了更好地预防天疱疮的发生,我们需要了解其成因及传播途径,并采取相应的预防措施。以下是关于天疱疮预防的小知识。 一、了解天疱疮 天疱疮是一种自身免疫性疾病&#…

神经网络代码入门解析

神经网络代码入门解析 import torch import matplotlib.pyplot as pltimport randomdef create_data(w, b, data_num): # 数据生成x torch.normal(0, 1, (data_num, len(w)))y torch.matmul(x, w) b # 矩阵相乘再加bnoise torch.normal(0, 0.01, y.shape) # 为y添加噪声…

解决android studio(ladybug版本) gradle的一些task突然消失了

今天不知道干了啥,AS(ladybug版本)右边gradle的task有些不见了,研究了半天解决了,这里记录下: 操作: File -->Settings-->Experimental--> 取消选项“Enable support for multi-vari…