python excel接口自动化测试框架

前言

前些天写了pytest+yaml+allure接口自动化测试框架这篇文章。

今天采用Excel继续写一个接口自动化测试框架。

设计流程图

这张图是我的excel接口测试框架的一些设计思路。

首先读取excel文件,得到测试信息,然后通过封装的requests方法,用unittest进行测试。

其中,接口关联的参数通过正则进行查找和替换,为此我专门开辟了一个全局变量池,用于管理各种各样的变量。

最后通过HTMLrunner生成测试报告。如果执行失败,发送测试报告结果邮件。

Excel和结果预览

这个时excel的测试测试用例组织结构图。

这个是运行之后生成的HTML测试报告。

这个时运行之后生成的excel报告。可以看到我故意在预期正则中设置了错误的值,然后用例失败的同时也把失败的预期值标记出来了。

img
测试失败之后收到的邮件
在这里插入图片描述

好了上面就是一些简单的介绍,我们开始进入正题把。

框架结构

首先,要开发这样一个excel接口自动化测试项目必须有一个设计清晰的思路,这样我们在开发框架的过程中才会明白自己要干什么。

目录/文件说明是否为python包
common公共类
core核心类,封装requests
data测试使用的excel文件存放目录
logs日志目录
tests测试用例目录
utils工具类,如:日志
config.py配置文件
run.py执行文件

Excel相关

用例设计

本次依然采用的是智学网登录接口。使用了智学网中的登录接口和登录验证接口,这两个接口之间有依赖的参数。

配置文件

在项目的根目录创建config.py,把你能想到的配置信息,全部丢在这个文件中进行统一的管理。

#!/usr/bin/env python3
# coding=utf-8
import os


class CF:
    """配置文件"""
    # 项目目录
    BASE_DIR = os.path.abspath(os.path.dirname(__file__))

    # Excel首行配置
    NUMBER = 0
    NAME = 1
    METHOD = 2
    URL = 3
    ROUTE = 4
    HEADERS = 5
    PARAMETER = 6  # 参数
    EXPECTED_CODE = 7  # 预期响应码
    EXPECTED_REGULAR = 8  # 预期正则
    EXPECTED_VALUE = 9  # 预期结果值
    SPEND_TIME = 10  # 响应时间
    TEST_RESULTS = 11  # 测试结果
    EXTRACT_VARIABLE = 12  # 提取变量
    RESPONSE_TEXT = 13  # 响应文本
    # 字体大小
    FONT_SET = "微软雅黑"
    FONT_SIZE = 16
    # 颜色配置
    COLOR_PASSED = "90EE90"
    COLOR_FAILED = "FA8072"

    # 邮箱配置
    EMAIL_INFO = {
        'username': '1084502012@qq.com',
        'password': 2,
        'smtp_host': 'smtp.qq.com',
        'smtp_port': 465
    }
    # 收件人
    ADDRESSEE = ['1084502012@qq.com']


if __name__ == '__main__':
    print(CF.EXPECTED_CODE)


读取/写入excel#

​ 在common目录中新建excelset.py文件,在这个文件中我们要实现,读取excel中的用例,写入测试结果并绘制相应的颜色,写入测试耗费时长。

#!/usr/bin/env python
# coding=utf-8
import shutil
import openpyxl
from config import CF
from openpyxl.styles import Font
from openpyxl.styles import PatternFill
from common.variables import VariablePool


class ExcelSet:
    """Excel配置"""

    def __init__(self):
        shutil.copyfile(VariablePool.get('excel_input'), VariablePool.get('excel_output'))
        self.path = VariablePool.get('excel_output')
        self.wb = openpyxl.load_workbook(self.path)
        self.table = self.wb.active

    def get_cases(self, min_row=2):
        """获取用例"""
        all_cases = []
        for row in self.table.iter_rows(min_row=min_row):
            all_cases.append((self.table.cell(min_row, CF.NAME + 1).value,
                              min_row, [cell.value for cell in row]))
            min_row += 1
        return all_cases

    def write_color(self, row_n, col_n, color=CF.COLOR_FAILED):
        """写入颜色"""
        cell = self.table.cell(row_n, col_n + 1)
        fill = PatternFill("solid", fgColor=color)
        cell.fill = fill

    def write_results(self, row_n, col_n, value, color=True):
        """写入结果"""
        cell = self.table.cell(row_n, col_n + 1)
        cell.value = value
        font = Font(name=CF.FONT_SET, size=CF.FONT_SIZE)
        cell.font = font
        if color:
            if value.lower() in ("fail", 'failed'):
                fill = PatternFill("solid", fgColor=CF.COLOR_FAILED)
                cell.fill = fill
            elif value.lower() in ("pass", "ok"):
                fill = PatternFill("solid", fgColor=CF.COLOR_PASSED)
                cell.fill = fill
        self.wb.save(self.path)


excel_set = ExcelSet()
if __name__ == '__main__':
    print(excel_set.get_cases())


日志封装

logger.py

在一个项目中日志是必不可少的东西,可以第一时间反馈问题。

#!/usr/bin/env python3
# coding=utf-8
import os
import logging
from config import CF
from datetime import datetime


class Logger:
    def __init__(self):
        self.logger = logging.getLogger()
        if not self.logger.handlers:
            self.logger.setLevel(logging.DEBUG)

            # 创建一个handler,用于写入日志文件
            fh = logging.FileHandler(self.log_path, encoding='utf-8')
            fh.setLevel(logging.DEBUG)

            # 创建一个handler,用于输出到控制台
            ch = logging.StreamHandler()
            ch.setLevel(logging.INFO)

            # 定义handler的输出格式
            formatter = logging.Formatter(self.fmt)
            fh.setFormatter(formatter)
            ch.setFormatter(formatter)

            # 给logger添加handler
            self.logger.addHandler(fh)
            self.logger.addHandler(ch)

    @property
    def log_path(self):
        logs_path = os.path.join(CF.BASE_DIR, 'logs')
        if not os.path.exists(logs_path):
            os.makedirs(logs_path)
        now_month = datetime.now().strftime("%Y%m")
        return os.path.join(logs_path, '{}.log'.format(now_month))

    @property
    def fmt(self):
        return '%(levelname)s %(asctime)s %(filename)s:%(lineno)d %(message)s'


log = Logger().logger
if __name__ == '__main__':
    log.info("你好")


正则操作

regular.py

在接口关联参数的提取和传参中的起到了决定性的作用。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import re
from utils.logger import log
from common.variables import VariablePool
from core.serialize import is_json_str


class Regular:
    """正则类"""

    def __init__(self):
        self.reg = re.compile

    def finds(self, string):
        return self.reg(r'\{{(.*?)}\}').findall(string)

    def subs(self, keys, string):
        result = None
        log.info("提取变量:{}".format(keys))
        for i in keys:
            if VariablePool.has(i):
                log.info("替换变量:{}".format(i))
                comment = self.reg(r"\{{%s}}" % i)
                result = comment.sub(VariablePool.get(i), string)
        log.info("替换结果:{}".format(result))
        return result

    def find_res(self, exp, string):
        """在结果中查找"""
        if is_json_str(string):
            return self.reg(r'\"%s":"(.*?)"' % exp).findall(string)[0]
        else:
            return self.reg(r'%s' % exp).findall(string)[0]


if __name__ == '__main__':
    a = "{'data': {'loginName': 18291900215, 'password': '{{dd636482aca022}}', 'code': None, 'description': 'encrypt'}}"
    print(Regular().finds(a))

核心操作

定义变量池

variables.py

全局变量池来了,是不是很简单,但是作用确实很巨大的。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-


class VariablePool:
    """全局变量池"""

    @staticmethod
    def get(name):
        """获取变量"""
        return getattr(VariablePool, name)

    @staticmethod
    def set(name, value):
        """设置变量"""
        setattr(VariablePool, name, value)

    @staticmethod
    def has(name):
        return hasattr(VariablePool, name)


if __name__ == '__main__':
    VariablePool.set('name', 'wxhou')
    print(VariablePool.get('name'))

封装requests

request.py

最最核心的部分,对于python requests库的二次封装。用以实现接口的请求和返回结果的获取。

#!/usr/bin/env python
# coding=utf-8
import urllib3
import requests
from config import CF
from utils.logger import log
from common.regular import Regular
from common.setResult import replace_param
from core.serialize import deserialization
from requests.exceptions import RequestException
from common.variables import VariablePool

urllib3.disable_warnings()


class HttpRequest:
    """二次封装requests方法"""

    http_method_names = 'get', 'post', 'put', 'delete', 'patch', 'head', 'options'

    def __init__(self):
        self.r = requests.session()
        self.reg = Regular()

    def send_request(self, case, **kwargs):
        """发送请求
        :param case: 测试用例
        :param kwargs: 其他参数
        :return: request响应
        """
        if case[CF.URL]:
            VariablePool.set('url', case[CF.URL])
        if case[CF.HEADERS]:
            VariablePool.set('headers', deserialization(case[CF.HEADERS]))

        method = case[CF.METHOD].upper()
        url = VariablePool.get('url') + case[CF.ROUTE]
        self.r.headers = VariablePool.get('headers')
        params = replace_param(case)
        if params: kwargs = params
        try:
            log.info("Request Url: {}".format(url))
            log.info("Request Method: {}".format(method))
            log.info("Request Data: {}".format(kwargs))

            def dispatch(method, *args, **kwargs):
                if method in self.http_method_names:
                    handler = getattr(self.r, method)
                    return handler(*args, **kwargs)
                else:
                    raise AttributeError('request method is ERROR!')
            response = dispatch(method.lower(), url, **kwargs)
            log.info(response)
            log.info("Response Data: {}".format(response.text))
            return response
        except RequestException as e:
            log.exception(format(e))
        except Exception as e:
            raise e

序列化与反序列化

serialize.py

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import json
from json.decoder import JSONDecodeError


def deserialization(content: json):
    """
    反序列化
        json对象 -> python数据类型
    """
    return json.loads(content)


def serialization(content, ensure_ascii=True):
    """
    序列化
        python数据类型 -> json对象
    """
    return json.dumps(content, ensure_ascii=ensure_ascii)


def is_json_str(string):
    """判断是否是json格式字符串"""
    if isinstance(string, str):
        try:
            json.loads(string)
            return True
        except JSONDecodeError:
            return False
    return False


if __name__ == '__main__':
    a = "{'data': {'loginName': 18291900215, 'password': 'dd636482aca022', 'code': None, 'description': 'encrypt'}}"
    print(is_json_str(a))

检查结果

checkResult.py

在这个文件中,我们将对测试返回的结果进行预期的验证。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import re
from config import CF
from utils.logger import log
from requests import Response
from common.excelset import excel_set


def check_result(r: Response, number, case):
    """获取结果"""
    results = []
    excel_set.write_results(number, CF.SPEND_TIME, r.elapsed.total_seconds(), color=False)
    if case[CF.EXPECTED_CODE]:
        res = int(case[CF.EXPECTED_CODE]) == r.status_code
        results.append(res)
        if not res: excel_set.write_color(number, CF.EXPECTED_CODE)
        log.info(f"预期响应码:{case[CF.EXPECTED_CODE]},实际响应码:{r.status_code}")
    if case[CF.EXPECTED_VALUE]:
        res = case[CF.EXPECTED_VALUE] in r.text
        results.append(res)
        if not res: excel_set.write_color(number, CF.EXPECTED_VALUE)
        log.info(f"预期响应值:{case[CF.EXPECTED_VALUE]},实际响应值:{r.text}")
    if case[CF.EXPECTED_REGULAR]:
        res = r'%s' % case[CF.EXPECTED_REGULAR]
        ref = re.findall(res, r.text)
        results.append(ref)
        if not ref: excel_set.write_color(number, CF.EXPECTED_REGULAR)
        log.info(f"预期正则:{res},响应{ref}")
    if all(results):
        excel_set.write_results(number, CF.TEST_RESULTS, 'Pass')
        log.info(f"用例【{case[CF.NAME]}】测试成功!")
    else:
        excel_set.write_results(number, CF.TEST_RESULTS, 'Failed')
        assert all(results), f"用例【{case[CF.NUMBER]}{case[CF.NAME]}】测试失败:{results}"


设置参数

setResult.py

在这个文件中我们实现了接口返回值的提取,实现了接口传递参数的函数。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from requests import Response
from utils.logger import log
from common.regular import Regular
from common.excelset import excel_set
from common.variables import VariablePool
from core.serialize import is_json_str, deserialization
from config import CF

reg = Regular()


def get_var_result(r: Response, number, case):
    """替换变量"""
    if case[CF.EXTRACT_VARIABLE]:
        for i in case[CF.EXTRACT_VARIABLE].split(','):
            result = reg.find_res(i, r.text)
            VariablePool.set(i, result)
            log.info(f"提取变量{i}={result}")
            if not VariablePool.get(i):
                excel_set.write_results(number, CF.EXTRACT_VARIABLE, f"提变量{i}失败")
    excel_set.write_results(number, CF.RESPONSE_TEXT,
                            f"ResponseCode:{r.status_code}\nResponseText:{r.text}")


def replace_param(case):
    """传入参数"""
    if case[CF.PARAMETER]:
        if is_json_str(case[CF.PARAMETER]):
            is_extract = reg.finds(case[CF.PARAMETER])
            if is_extract:
                return deserialization(reg.subs(is_extract, case[CF.PARAMETER]))
    return deserialization(case[CF.PARAMETER])


测试操作

test_api.py

我们采用unittest进行测试,在前置条件和后置条件中我们对封装的HttpRequest方法进行了初始化和关闭会话操作。

使用parameterized库中的expend方法对excel中的用例进行参数化读取执行。

#!/usr/bin/env python
# coding=utf-8
import unittest
from parameterized import parameterized
from common.excelset import excel_set
from core.request import HttpRequest
from common.checkResult import check_result
from common.setResult import get_var_result


class TestApi(unittest.TestCase):
    """测试接口"""

    @classmethod
    def setUpClass(cls) -> None:
        cls.req = HttpRequest()

    @classmethod
    def tearDownClass(cls) -> None:
        cls.req.r.close()

    @parameterized.expand(excel_set.get_cases())
    def test_api(self, name, number, case):
        """
        测试excel接口用例
        """
        r = self.req.send_request(case)
        get_var_result(r, number, case)
        check_result(r, number, case)


if __name__ == '__main__':
    unittest.main(verbosity=2)

测试报告发送邮件类

run.py

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import os
import platform
import argparse
import unittest
from common.variables import VariablePool
from utils.send_mail import send_report_mail
from utils.HTMLTestRunner import HTMLTestRunner


def running(path):
    """运行"""
    test_case = unittest.defaultTestLoader.discover('tests', 'test*.py')
    with open(path, 'wb') as fp:
        runner = HTMLTestRunner(stream=fp,
                                title='Excel接口测试',
                                description="用例执行情况",
                                verbosity=2)
        result = runner.run(test_case)
    if result.failure_count:
        send_report_mail(path)


def file_path(arg):
    """获取输入的文件路径"""
    if 'Windows' in platform.platform():
        _dir = os.popen('chdir').read().strip()
    else:
        _dir = os.popen('pwd').read().strip()
    if _dir in arg:
        return arg
    return os.path.join(_dir, arg)


def main():
    """主函数"""
    parser = argparse.ArgumentParser(description="运行Excel接口测试")
    parser.add_argument('-i', type=str, help='原始文件')
    parser.add_argument('-o', type=str, default='report.xlsx', help="输出文件")
    parser.add_argument('-html', type=str, default='report.html', help="报告文件")
    args = parser.parse_args()
    VariablePool.set('excel_input', file_path(args.i))
    VariablePool.set('excel_output', file_path(args.o))
    VariablePool.set('report_path', file_path(args.html))
    running(VariablePool.get('report_path'))


if __name__ == '__main__':
    main()



运行

值得注意的是,运行测试时要关闭office打开该excel文件。

最后的文件中我是使用了argparse进行了命令行管理,意味着我们可以通过命令行进行测试而无需关心excel在那个目录下存放着。

python run.py -i data\usercase.xlsx

输入下面的命令执行一下。

INFO 2020-07-30 22:07:52,713 request.py:40 Request Url: https://www.zhixue.com/weakPwdLogin/?from=web_login
INFO 2020-07-30 22:07:52,714 request.py:41 Request Method: POST
INFO 2020-07-30 22:07:52,715 request.py:42 Request Data: {'data': {'loginName': 18291900215, 'password': 'dd636482aca022', 'code': None, 'descriptio
n': 'encrypt'}}
INFO 2020-07-30 22:08:17,204 request.py:55 <Response [200]>
INFO 2020-07-30 22:08:17,204 request.py:56 Response Data: {"data":"1500000100070008427","result":"success"}
INFO 2020-07-30 22:08:17,207 setResult.py:20 提取变量data=1500000100070008427
INFO 2020-07-30 22:08:17,307 checkResult.py:18 预期响应码:200,实际响应码:200
INFO 2020-07-30 22:08:17,308 checkResult.py:23 预期响应值:"result":"success",实际响应值:{"data":"1500000100070008427","result":"success"}
INFO 2020-07-30 22:08:17,310 checkResult.py:29 预期正则:[\d]{16},响应['1500000100070008']
INFO 2020-07-30 22:08:17,356 checkResult.py:32 用例【登录】测试成功!
ok test_api_0__ (test_api.TestApi)
INFO 2020-07-30 22:08:17,358 regular.py:20 提取变量:['data']
INFO 2020-07-30 22:08:17,359 regular.py:23 替换变量:data
INFO 2020-07-30 22:08:17,361 regular.py:26 替换结果:{"data": {"userId": "1500000100070008427"}}
INFO 2020-07-30 22:08:17,363 request.py:40 Request Url: https://www.zhixue.com/loginSuccess/
INFO 2020-07-30 22:08:17,366 request.py:41 Request Method: POST
INFO 2020-07-30 22:08:17,367 request.py:42 Request Data: {'data': {'userId': '1500000100070008427'}}
INFO 2020-07-30 22:08:20,850 request.py:55 <Response [200]>
INFO 2020-07-30 22:08:20,851 request.py:56 Response Data: {"result":"success"}
INFO 2020-07-30 22:08:20,932 checkResult.py:18 预期响应码:200,实际响应码:200
INFO 2020-07-30 22:08:20,933 checkResult.py:23 预期响应值:"result":"success",实际响应值:{"result":"success"}
INFO 2020-07-30 22:08:20,935 checkResult.py:29 预期正则:11,响应[]
F  test_api_1__ (test_api.TestApi)

Time Elapsed: 0:00:28.281434
测试结果邮件发送成功!

执行规则

(venv) C:\Users\hoou\PycharmProjects\httptest-excel>python run.py -h
usage: run.py [-h] [-i I] [-o O] [-html HTML]

运行Excel接口测试

optional arguments:
  -h, --help  show this help message and exit
  -i I        原始文件
  -o O        输出文件
  -html HTML  报告文件

SHELL 复制 全屏

在命令行输入python run.py excel路径 新excel路径 报告路径

如果不输入新excel路径报告路径,会在run.py所在目录生成两个report.xlsx,report.html。

本篇的excel测试框架就完成了。

Python接口自动化测试零基础入门到精通(2023最新版)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++数据结构X篇_21_插入排序(稳定的排序)

文章目录 1. 插入排序原理2. 算法图解3. 核心代码&#xff1a;4. 插入排序整体代码实现 1. 插入排序原理 插入排序是一种最简单直观的排序算法&#xff0c;它的工作原理是通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相…

线程间的调度顺序

目录 ♫join和sleep ♫wait ♫notify和notifyAll 我们知道线程是抢占式执行&#xff0c;随机调度的&#xff0c;而这也是诱发线程安全的根本原因。我们虽然无法指定线程之间的调度顺序&#xff0c;但是可以通过JVM提供的一些API让某个线程阻塞&#xff0c;主动放弃CPU&#xf…

【31】c++设计模式——>模板方法模式

模板方法模式通常由以下几个部分组成&#xff1a; 1.抽象基类&#xff08;Abstract Base Class&#xff09;&#xff1a;抽象基类定义了一个算法的骨架&#xff0c;其中包含了模板方法和一些基本操作方法。模板方法在抽象基类中被声明为虚函数&#xff0c;它定义了算法的流程&…

CentOS - 安装 Elasticsearch

"Elasticsearch"是一个流行的开源搜索和分析引擎&#xff0c;它可以用于实时搜索、日志和事件数据分析等任务。以下是在 CentOS 上安装 Elasticsearch 的基本步骤&#xff1a; 安装 Java&#xff1a; Elasticsearch 是基于 Java 的应用程序&#xff0c;所以首先需要…

openwrt下游设备在校园网(DLUT-LingShui)中使用ipv6网络

背景&#xff1a;校园网最多支持6台设备的无感认证&#xff0c;需要使用路由器(本人使用openwrt系统)为更多的设备提供网络&#xff0c;但校园网分配的ipv6地址子网为/128&#xff0c;不能为路由器下的设备分配全球ipv6地址&#xff0c;因此需要使用nat6转发下游设备的局域网ip…

el-table多选表格 实现默认选中 删除选中列表取消勾选等联动效果

实现效果如下&#xff1a; 代码如下&#xff1a; <template><div><el-tableref"multipleTable":data"tableData"tooltip-effect"dark"style"width: 100%"selection-change"handleSelectionChange"><…

Generative AI 新世界 | Falcon 40B 开源大模型的部署方式分析

在上期文章&#xff0c;我们探讨了如何在自定义数据集上来微调&#xff08;fine-tuned&#xff09;模型。本期文章&#xff0c;我们将重新回到文本生成的大模型部署场景&#xff0c;探讨如何在 Amazon SageMaker 上部署具有 400 亿参数的 Falcon 40B 开源大模型。 亚马逊云科技…

IC-705连接wfview

wfview是一款开源的主要针对ICOM的远程控制软件&#xff0c;可以通过USB或者无线控制电台&#xff0c;貌似还支持X6100。 IC-705支持WLAN功能&#xff0c;连接wfview非常方便。 IC-705的WLAN支持两种模式&#xff0c;一种是Station模式&#xff0c;可用于连接WI-FI路由器&#…

【考研数学】数学“背诵”手册 | 需要记忆且容易遗忘的知识点

文章目录 引言一、高数常见泰勒展开 n n n 阶导数公式多元微分函数连续、可微、连续可偏导之间的关系多元函数极值无条件极值条件极值 三角函数的积分性质华里士公式&#xff08; “点火”公式 &#xff09;特殊性质 原函数与被积函数的奇偶性结论球坐标变换公式 二、写在最后 …

python自动化测试(四):ECShop后台:商品分类添加

前置条件&#xff1a; 本地部署&#xff1a;ECShop的版本是3.0.0、Google版本是 Google Chrome65.0.3325.162 (正式版本) &#xff08;32 位&#xff09; Google驱动的selenium版本是3.11.0 目录 前置代码 一、登录&#xff08;后台登录&#xff09; 二、进入商品分类页…

JavaScript笔记(本文中将JavaScript简写为JS)

JS对大小写敏感 JS代码块的作用域都是全局的 JS的数组只能使用数字作为下标 JS对浮点型数据的精确度很难确定 JS在定义数组元素以及对象&#xff0c;在最后不能添加逗号 JS 中&#xff0c;变量可以在使用后声明&#xff0c;也就是变量可以先使用再声明&#xff0c;但不适用于已…

wkhtmltoimage/wkhtmltopdf 使用实践

1. 介绍 wkhtmltopdf/wkhtmltoimage 用于将简单的html页面转换为pdf或图片&#xff1b; 2.安装 downloads 2.1. mac os 下载64-bit 版本然后按照指示安装, 遇到 untrust developers 时&#xff0c;需要在 Settings -> Privacy 处信任下该安装包。 2.2. debian # 可用…

vscode代码快捷输入

Vscode代码片段快捷输入 常用的代码片段为了避免重复输入,可以使用Vsco的中用户代码片段进行设置,这样就可以实现快捷输入. 操作流程 如下 打开vscode的设置 2. 找到用户代码片段 3. 选择模板 4. 然后写入代码片段即可 上面的代码片段可以设置多个,看自己 重点关注的是 prefi…

05 MIT线性代数-转置,置换,向量空间Transposes, permutations, spaces

1. Permutations P: execute row exchanges becomes PA LU for any invertible A Permutations P identity matrix with reordered rows mn (n-1) ... (3) (2) (1) counts recordings, counts all nxn permuations 对于nxn矩阵存在着n!个置换矩阵 , 2. Transpose: 2.…

p5.js 3D图形-立方体

本文简介 带尬猴&#xff0c;我嗨德育处主任 前面写了几篇 p5.js 文章 都还没涉及到3D图形&#xff0c;但其实 p5.js 是提供了基础的3D图形的。 本文就从最简单的立方体讲起&#xff0c;并做几个小demo和各位工友一起掌握立方体的用法。 立方体的基础用法 在 p5.js 里使用 b…

Ubuntu 内核降级到指定版本

reference https://www.cnblogs.com/leebri/p/16786685.html 前往此网站&#xff0c;找到所需的内核 https://kernel.ubuntu.com/~kernel-ppa/mainline/ 查看系统架构 dpkg --print-architecture 二、下载安装包 注意&#xff1a;下载除lowlatency以外的deb包 三、安装内核 3…

基于图像识别的跌倒检测算法 计算机竞赛

前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于图像识别的跌倒检测算法 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/…

FreeRTOS 事件标志组 详解

目录 什么是事件标志组&#xff1f; 事件标志位 事件标志组 事件标志组相关 API 函数 1. 创建事件标志组 2. 设置事件标志位 3. 清除事件标志位 4. 等待事件标志位 事件标志组实操 什么是事件标志组&#xff1f; 事件标志位 表明某个事件是否发生&#xff0c;联想&am…

优咔科技创新连接方案助力高质量5G车联服务

上海优咔网络科技有限公司 CEO 闫楠 【摘要】本文就智能网联汽车对高质量5G车联服务的需求背景和行业趋势进行了分析&#xff0c;主要介绍采用5G双SIM卡的创新连接方案&#xff0c;重点讲述双SIM卡联网的端到端体系架构和技术方案&#xff0c;并就优咔科技全方位支撑行业领先车…

设计模式—创建型模式之单例模式

设计模式—创建型模式之单例模式 介绍 单例模式说明&#xff1a;一个单一的类&#xff0c;负责创建自己的对象&#xff0c;同时确保系统中只有单个对象被创建。 单例模式特点&#xff1a; 某个类只能有一个实例&#xff1b;&#xff08;构造器私有&#xff09;它必须自行创…