Selenium+Pytest自动化测试框架实战

前言

Selenium和Pytest是非常流行的自动化测试框架,二者的配合可以有效地进行Web应用程序的测试。在使用Selenium和Pytest进行自动化测试时,可以实现自动化测试的模块化、重复使用,并且可以在现有的测试项目中方便地集成和调用。

测试框架简介

测试框架有什么优点呢:

  • 代码复用率高,如果不使用框架的话,代码会很冗余

  • 可以组装日志、报告、邮件等一些高级功能

  • 提高元素等数据的可维护性,元素发生变化时,只需要更新一下配置文件

  • 使用更灵活的PageObject设计模式

测试框架的整体目录

目录/文件说明是否为python包
common这个包中存放的是常见的通用的类 是
config配置文件目录
logs日志目录
page对selenium的方放进行深度的封装
page_element页面元素存放目录
page_object 页面对象POM设计模式,本人对这个的理解来自于苦叶子的博客
report 报告文件目录
TestCase 所有的测试用例集
tools工具类
conftest.py 常用目录等
pytest.inipytest配置文件

这样一个简单的框架结构就清晰了。

知道了以上这些我们就开始吧!

我们在项目中先按照上面的框架指引,建好每一项目录。

注意:python包为是的,都需要添加一个__init__.py文件以标识此目录为一个python包。

添加配置文件

  • 配置文件总是项目中必不可少的部分!

  • 将固定不变的信息集中在固定的文件中

conf.py

项目中都应该有一个文件对整体的目录进行管理,我也在这个python项目中设置了此文件。

在项目config目录创建conf.py文件,所有的目录配置信息写在这个文件里面。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import os
from selenium.webdriver.common.by import By
# 项目目录
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 配置文件
INI_PATH = os.path.join(BASE_DIR, 'config', 'config.ini')
# 页面元素目录
ELEMENT_PATH = os.path.join(BASE_DIR, 'page_element')
# 日志目录
LOG_PATH = os.path.join(BASE_DIR, 'logs')
# 报告目录
REPORT_PATH = os.path.join(BASE_DIR, 'report', 'report.html')
# 元素定位的类型
LOCATE_MODE = {
    'css': By.CSS_SELECTOR,
    'xpath': By.XPATH,
    'name': By.NAME,
    'id': By.ID,
    'class': By.CLASS_NAME
}
# 邮件信息
EMAIL_INFO = {
    'username': '1084502012@qq.com',  # 切换成你自己的地址
    'password': 'QQ邮箱授权码',
    'smtp_host': 'smtp.qq.com',
    'smtp_port': 465
}
# 收件人
ADDRESSEE = [
    '1084502012@qq.com',
]
if __name__ == '__main__':
    print(BASE_DIR)

注意:QQ邮箱授权码

这个conf文件我模仿了Django的settings.py文件风格。

在这个文件中我们可以设置自己的各个目录,也可以查看自己当前的目录。

遵循了约定:不变的常量名全部大写,变量名称小写的规范。看起来整体美观。

config.ini

在项目config目录新建一个config.ini文件,里面暂时先放入我们的需要测试的URL

[HOST]
HOST = https://www.baidu.com

读取配置文件

配置文件创建好了,接下来我们需要读取这个配置文件以使用里面的信息。

我们在common目录中新建一个readconfig.py文件

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import os
import configparser
from config.conf import INI_PATH
HOST = 'HOST'
class ReadConfig:
    """配置文件"""
    def __init__(self):
        if not os.path.exists(INI_PATH):
            raise FileNotFoundError("配置文件%s不存在!" % INI_PATH)
        self.config = configparser.RawConfigParser()  # 当有%的符号时请使用Raw读取
        self.config.read(INI_PATH, encoding='utf-8')
    def _get(self, section, option):
        """获取"""
        return self.config.get(section, option)
    def _set(self, section, option, value):
        """更新"""
        self.config.set(section, option, value)
        with open(INI_PATH, 'w') as f:
            self.config.write(f)
    @property
    def url(self):
        return self._get(HOST, HOST)
ini = ReadConfig()
if __name__ == '__main__':
    print(ini.url)

可以看到我们用python内置的configparser模块对config.ini文件进行了读取。

对于url值的提取,我使用了高阶语法@property属性值,写法更简单。

时间管理起来

因为用到时间的地方可能会比较多,所以我们也单独把时间封装成一个模块。让其他模块来调用即可。

在tools目录新建times.py模块

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import time
import datetime
def timestamp():
    """时间戳"""
    return time.time()
def datetime_strftime(fmt="%Y%m"):
    """datetime格式化时间"""
    return datetime.datetime.now().strftime(fmt)
def sleep(seconds=1.0):
    """
    睡眠时间
    """
    time.sleep(seconds)
if __name__ == '__main__':
    print(datetime_strftime())

记录操作日志

日志,大家应该都很熟悉这个名词,就是记录代码中的动作。

在tools目录中新建logger.py文件。

这个文件就是我们用来在自动化测试过程中记录一些操作步骤的。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import os
import logging
from config.conf import LOG_PATH
from tools.times import datetime_strftime
class Log:
    def __init__(self):
        self.logger = logging.getLogger()
        if not self.logger.handlers:
            self.logger.setLevel(logging.DEBUG)
            # 创建一个handle写入文件
            fh = logging.FileHandler(self.log_path, encoding='utf-8')
            fh.setLevel(logging.INFO)
            # 创建一个handle输出到控制台
            ch = logging.StreamHandler()
            ch.setLevel(logging.INFO)
            # 定义输出的格式
            formatter = logging.Formatter(self.fmt)
            fh.setFormatter(formatter)
            ch.setFormatter(formatter)
            # 添加到handle
            self.logger.addHandler(fh)
            self.logger.addHandler(ch)
    @property
    def log_path(self):
        if not os.path.exists(LOG_PATH):
            os.makedirs(LOG_PATH)
        return os.path.join(LOG_PATH, '{}.log'.format(datetime_strftime()))
    @property
    def fmt(self):
        return '%(levelname)s\t%(asctime)s\t[%(filename)s:%(lineno)d]\t%(message)s'
log = Log().logger
if __name__ == '__main__':
    log.info('你好')

在终端中运行该文件,就看到命令行打印出了:

INFO    2020-01-16 17:07:17,655 [log.py:57]     你好

然后在项目logs目录下生成了当月的日志文件。

简单理解POM模型

由于下面要讲元素相关的,所以首先理解一下POM模型

Page Object模式具有以下几个优点。

  • 抽象出对象可以最大程度地降低开发人员修改页面代码对测试的影响, 所以, 你仅需要对页面对象进行调整, 而对测试没有影响;

  • 可以在多个测试用例中复用一部分测试代码;

  • 测试代码变得更易读、 灵活、 可维护

Page Object模式图

  • basepage ——selenium的基类,对selenium的方法进行封装

  • pageelements——页面元素,把页面元素单独提取出来,放入一个文件中

  • searchpage ——页面对象类,把selenium方法和页面元素进行整合

  • testcase ——使用pytest对整合的searchpage进行测试用例编写

通过上图我们可以看出,通过POM模型思想,我们把:

  • selenium方法

  • 页面元素

  • 页面对象

  • 测试用例

以上四种代码主体进行了拆分,虽然在用例很少的情况下做会增加代码,但是当用例多的时候意义很大,代码量会在用例增加的时候显著减少。我们维护代码变得更加直观明显,代码可读性也变得比工厂模式强很多,代码复用率也极大的得到了提高。

简单学习元素定位

在日常的工作中,我见过很多在浏览器中直接在浏览器中右键Copy Xpath复制元素的同学。这样获得的元素表达式放在 webdriver 中去运行往往是不够稳定的,像前端的一些微小改动,都会引起元素无法定位的NoSuchElementException报错。

所以在实际工作和学习中我们应该加强自己的元素定位能力,尽可能的采用xpath和CSS selector 这种相对稳定的定位语法。由于CSS selector的语法生硬难懂,对新手很不友好,而且相比xpath缺少一些定位语法。所以我们选择xpath进行我们的元素定位语法。

xpath

语法规则

表达式介绍备注
/根节点绝对路径
//当前节点的所有子节点相对路径
*所有节点元素的
@属性名的前缀@class   @id
*[1][] 下标运算符
[][ ]谓词表达式 //input[@id='kw']
Following-sibling当前节点之后的同级
preceding-sibling当前节点之前的同级
parent当前节点的父级节点

定位工具

  • chropath

优点:这是一个Chrome浏览器的测试定位插件,类似于firepath,本人试用了一下整体感觉非常好。对小白的友好度很好。

缺点:安装这个插件需要翻墙。

  • Katalon录制工具

录制出来的脚本里面也会有定位元素的信息

  • 自己写——本人推荐这种

优点:本人推荐的方式,因为当熟练到一定程度的时候,写出来的会更直观简洁,并且在运行自动化测试中出现问题时,能快速定位。

缺点:需要一定xpath和CSS selector语法积累,不太容易上手。

管理页面元素

项目框架设计中有一个目录page_element就是专门来存放定位元素的文件的。

通过对各种配置文件的对比,我在这里选择的是YAML文件格式。其易读,交互性好。

我们在page_element中新建一个search.yaml文件。

搜索框: "id==kw"
候选: "css==.bdsug-overflow"
搜索候选: "css==#form div li"
搜索按钮: "id==su"

元素定位文件创建好了,下来我们需要读取这个文件。

在common目录中创建readelement.py文件。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import os
import yaml
from config.conf import ELEMENT_PATH
class Element:
    """获取元素"""
    def __init__(self, name):
        self.file_name = '%s.yaml' % name
        self.element_path = os.path.join(ELEMENT_PATH, self.file_name)
        if not os.path.exists(self.element_path):
            raise FileNotFoundError("%s 文件不存在!" % self.element_path)
        with open(self.element_path, encoding='utf-8') as f:
            self.data = yaml.safe_load(f)
    def __getitem__(self, item):
        """获取属性"""
        data = self.data.get(item)
        if data:
            name, value = data.split('==')
            return name, value
        raise ArithmeticError("{}中不存在关键字:{}".format(self.file_name, item))
if __name__ == '__main__':
    search = Element('search')
    print(search['搜索框'])

通过特殊方法__getitem__实现调用任意属性,读取yaml中的值。

这样我们就实现了定位元素的存储和调用。

但是还有一个问题,我们怎么样才能确保我们写的每一项元素不出错,人为的错误是不可避免的,但是我们可以通过代码来运行对文件的审查。当前也不能所有问题都能发现。

所以我们编写一个文件,在common目录中创建inspect.py文件,对所有的元素yaml文件进行审查。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import os
import yaml
from tools.times import timestamp
from config.conf import ELEMENT_PATH, LOCATE_MODE
def inspect_element():
    """审查所有的元素是否正确"""
    start_time = timestamp()
    for i in os.listdir(ELEMENT_PATH):
        _path = os.path.join(ELEMENT_PATH, i)
        if os.path.isfile(_path):
            with open(_path, encoding='utf-8') as f:
                data = yaml.safe_load(f)
                for k in data.values():
                    pattern, value = k.split('==')
                    if pattern not in LOCATE_MODE:
                        raise AttributeError('【%s】路径中【%s]元素没有指定类型' % (i, k))
                    if pattern == 'xpath':
                        assert '//' in value, '【%s】路径中【%s]元素xpath类型与值不配' % (
                            i, k)
                    if pattern == 'css':
                        assert '//' not in value, '【%s】路径中【%s]元素css类型与值不配' % (
                            i, k)
                    if pattern in ('id', 'name', 'class'):
                        assert value, '【%s】路径中【%s]元素类型与值不匹配' % (i, k)
    end_time = timestamp()
    print("校验元素done!用时%.3f秒!" % (end_time - start_time))
if __name__ == '__main__':
    inspect_element()

执行该文件:

校验元素done!用时0.002秒!

可以看到,很短的时间内,我们就对所填写的YAML文件进行了审查。

现在我们基本所需要的组件已经大致完成了。

接下来我们将进行最重要的一环,封装selenium。

封装Selenium基类

在工厂模式种我们是这样写的:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import time
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
driver.find_element_by_xpath("//input[@id='kw']").send_keys('selenium')
driver.find_element_by_xpath("//input[@id='su']").click()
time.sleep(5)
driver.quit()

很直白,简单,又明了。

创建driver对象,打开百度网页,搜索selenium,点击搜索,然后停留5秒,查看结果,最后关闭浏览器。

那我们为什么要封装selenium的方法呢。首先我们上述这种较为原始的方法,基本不适用于平时做UI自动化测试的,因为在UI界面实际运行情况远远比较复杂,可能因为网络原因,或者控件原因,我们元素还没有显示出来,就进行点击或者输入。所以我们需要封装selenium方法,通过内置的显式等待或一定的条件语句,才能构建一个稳定的方法。而且把selenium方法封装起来,有利于平时的代码维护。

我们在page目录创建webpage.py文件。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from config.conf import LOCATE_MODE
from tools.times import sleep
from tools.logger import log
"""
selenium基类
本文件存放了selenium基类的封装方法
"""
class WebPage(object):
    """selenium基类"""
    def __init__(self, driver):
        # self.driver = webdriver.Chrome()
        self.driver = driver
        self.timeout = 20
        self.wait = WebDriverWait(self.driver, self.timeout)
    def get_url(self, url):
        """打开网址并验证"""
        self.driver.maximize_window()
        self.driver.set_page_load_timeout(60)
        try:
            self.driver.get(url)
            self.driver.implicitly_wait(10)
            log.info("打开网页:%s" % url)
        except TimeoutException:
            raise TimeoutException("打开%s超时请检查网络或网址服务器" % url)
    @staticmethod
    def element_locator(func, locator):
        """元素定位器"""
        name, value = locator
        return func(LOCATE_MODE[name], value)
    def find_element(self, locator):
        """寻找单个元素"""
        return WebPage.element_locator(lambda *args: self.wait.until(
            EC.presence_of_element_located(args)), locator)
    def find_elements(self, locator):
        """查找多个相同的元素"""
        return WebPage.element_locator(lambda *args: self.wait.until(
            EC.presence_of_all_elements_located(args)), locator)
    def elements_num(self, locator):
        """获取相同元素的个数"""
        number = len(self.find_elements(locator))
        log.info("相同元素:{}".format((locator, number)))
        return number
    def input_text(self, locator, txt):
        """输入(输入前先清空)"""
        sleep(0.5)
        ele = self.find_element(locator)
        ele.clear()
        ele.send_keys(txt)
        log.info("输入文本:{}".format(txt))
    def is_click(self, locator):
        """点击"""
        self.find_element(locator).click()
        sleep()
        log.info("点击元素:{}".format(locator))
    def element_text(self, locator):
        """获取当前的text"""
        _text = self.find_element(locator).text
        log.info("获取文本:{}".format(_text))
        return _text
    @property
    def get_source(self):
        """获取页面源代码"""
        return self.driver.page_source
    def refresh(self):
        """刷新页面F5"""
        self.driver.refresh()
        self.driver.implicitly_wait(30)
if __name__ == "__main__":
    pass

在文件中我们对主要用了显式等待对selenium的click,send_keys等方法,做了二次封装。提高了运行的成功率。

好了我们完成了POM模型的一半左右的内容。接下来我们们进入页面对象。

创建页面对象

在page_object目录下创建一个searchpage.py文件。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from page.webpage import WebPage, sleep
from common.readelement import Element
search = Element('search')
class SearchPage(WebPage):
    """搜索类"""
    def input_search(self, content):
        """输入搜索"""
        self.input_text(search['搜索框'], txt=content)
        sleep()
    @property
    def imagine(self):
        """搜索联想"""
        return [x.text for x in self.find_elements(search['候选'])]
    def click_search(self):
        """点击搜索"""
        self.is_click(search['搜索按钮'])
if __name__ == '__main__':
    pass

在该文件中我们对,输入搜索关键词,点击搜索,搜索联想,进行了封装。

并配置了注释。

好了我们的页面对象此时业已完成了。下面我们开始编写测试用例。在开始测试用了之前我们先熟悉一下pytest测试框架。

简单了解Pytest

打开pytest框架的官网。http://www.pytest.org/en/latest/

# content of test_sample.py
def inc(x):
    return x + 1
def test_answer():
    assert inc(3) == 5

官方教程我认为写的并不适合入门阅读,而且没有汉化版。

推荐看一下上海悠悠的pytest教程。

pytest.ini

pytest项目中的配置文件,可以对pytest执行过程中操作做全局控制。

在项目根目录新建pytest.ini文件。

[pytest]
addopts = -s -v --html=report/report.html --self-contained-html
  • addopts 指定执行时附带的参数

-s 输出我们用例中的调式信息

-q 安静的进行测试

-v 可以输出用例更加详细的执行信息,比如用例所在的文件及用例名称等

--html=report/report.html --self-contained-html 生成pytest-html带样式的报告

编写测试用例

我们将使用pytest编写测试用例。

在TestCase目录中创建test_search.py文件。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import re
import pytest
from tools.logger import log
from common.readconfig import ini
from page_object.searchpage import SearchPage
class TestSearch:
    @pytest.fixture(scope='function', autouse=True)
    def open_baidu(self, drivers):
        """打开百度"""
        search = SearchPage(drivers)
        search.get_url(ini.url)
    def test_001(self, drivers):
        """搜索"""
        search = SearchPage(drivers)
        search.input_search("selenium")
        search.click_search()
        result = re.search(r'selenium', search.get_source)
        log.info(result)
        assert result
    def test_002(self, drivers):
        """测试搜索候选"""
        search = SearchPage(drivers)
        search.input_search("selenium")
        log.info(list(search.imagine))
        assert all(["selenium" in i for i in search.imagine])
if __name__ == '__main__':
    pytest.main(['TestCase/test_search.py'])

我们测试用了就编写好了。

  • pytest.fixture 这个实现了和unittest的setup,teardown一样的前置启动,后置清理的装饰器。

  • 第一个测试用例:

我们实现了在百度selenium关键字,并点击搜索按钮,并在搜索结果中,用正则查找结果页源代码,返回数量大于10我们就认为通过。

  • 第二个测试用例:

我们实现了,搜索selenium,然后断言搜索候选中的所有结果有没有selenium关键字。

最后我们的在下面写一个执行启动的语句。

这时候我们应该进入执行了,但是还有一个问题,我们还没有把driver传递。

conftest.py

我们在项目根目录下新建一个conftest.py文件。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import pytest
from py._xmlgen import html
from selenium import webdriver
from common.inspect import inspect_element
driver = None
@pytest.fixture(scope='session', autouse=True)
def drivers(request):
    global driver
    if driver is None:
        driver = webdriver.Chrome()
        driver.maximize_window()
inspect_element()
    def fn():
        driver.quit()
    request.addfinalizer(fn)
    return driver
@pytest.mark.hookwrapper
def pytest_runtest_makereport(item):
    """
    当测试失败的时候,自动截图,展示到html报告中
    :param item:
    """
    pytest_html = item.config.pluginmanager.getplugin('html')
    outcome = yield
    report = outcome.get_result()
    extra = getattr(report, 'extra', [])
    if report.when == 'call' or report.when == "setup":
        xfail = hasattr(report, 'wasxfail')
        if (report.skipped and xfail) or (report.failed and not xfail):
            file_name = report.nodeid.replace("::", "_") + ".png"
            screen_img = _capture_screenshot()
            if file_name:
                html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:1024px;height:768px;" ' \
                       'onclick="window.open(this.src)" align="right"/></div>' % screen_img
                extra.append(pytest_html.extras.html(html))
        report.extra = extra
        report.description = str(item.function.__doc__)
        report.nodeid = report.nodeid.encode("utf-8").decode("unicode_escape")
@pytest.mark.optionalhook
def pytest_html_results_table_header(cells):
    cells.insert(1, html.th('用例名称'))
    cells.insert(2, html.th('Test_nodeid'))
    cells.pop(2)
@pytest.mark.optionalhook
def pytest_html_results_table_row(report, cells):
    cells.insert(1, html.td(report.description))
    cells.insert(2, html.td(report.nodeid))
    cells.pop(2)
@pytest.mark.optionalhook
def pytest_html_results_table_html(report, data):
    if report.passed:
        del data[:]
        data.append(html.div('通过的用例未捕获日志输出.', class_='empty log'))
def _capture_screenshot():
    '''
    截图保存为base64
    :return:
    '''
    return driver.get_screenshot_as_base64()

conftest.py测试框架pytest的胶水文件,里面用到了fixture的方法,封装并传递出了driver。

执行用例

以上我们已经编写完成了整个框架和测试用例。

我们进入到当前项目的主目录执行命令:

pytest

命令行输出:

Test session starts (platform: win32, Python 3.7.7, pytest 5.3.2, pytest-sugar 0.9.2)
cachedir: .pytest_cache
metadata: {'Python': '3.7.7', 'Platform': 'Windows-10-10.0.18362-SP0', 'Packages': {'pytest': '5.3.2', 'py': '1.8.0', 'pluggy': '0.13.1'}, 'Plugins': {'forked': '1.1.3', 'html': '2.0.1', 'metadata': '1.8.0', 'ordering': '0.6', 'rerunfailures': '8.0', 'sugar': '0.9.2', 'xdist': '1.31.0'}, 'JAVA_HOME': 'D:\\Program Files\\Java\\jdk1.8.0_131'}
rootdir: C:\Users\hoou\PycharmProjects\web-demotest, inifile: pytest.ini
plugins: forked-1.1.3, html-2.0.1, metadata-1.8.0, ordering-0.6, rerunfailures-8.0, sugar-0.9.2, xdist-1.31.0
collecting ... 
DevTools listening on ws://127.0.0.1:10351/devtools/browser/78bef34d-b94c-4087-b724-34fb6b2ef6d1
 TestCase\test_search.py::TestSearch.test_001 ?                                                                                              50% █████     
 TestCase\test_search.py::TestSearch.test_002 ?                                                                                             100% ██████████
------------------------------- generated html file: file://C:\Users\hoou\PycharmProjects\web-demotest\report\report.html -------------------------------- 
Results (12.90s):
       2 passed

可以看到两条用例已经执行成功了。

项目的report目录中生成了一个report.html文件。

这就是生成的测试报告文件。

发送邮件

当项目执行完成之后,需要发送到自己或者其他人邮箱里查看结果。

我们编写发送邮件的模块。

在tools目录中新建send_mail.py文件

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import zmail
from config.conf import REPORT_PATH, EMAIL_INFO, ADDRESSEE
def send_report():
    """发送报告"""
    with open(REPORT_PATH) as f:
        content_html = f.read()
    try:
        mail = {
            'from': '1084502012@qq.com',
            'subject': '最新的测试报告邮件',
            'content_html': content_html,
            'attachments': [REPORT_PATH, ]
        }
        server = zmail.server(*EMAIL_INFO.values())
        server.send_mail(ADDRESSEE, mail)
    except Exception as e:
        print("Error: 无法发送邮件,{}!", format(e))
    else:
        print("测试邮件发送成功!")
if __name__ == "__main__":
    '''请先在config/conf.py文件设置QQ邮箱的账号和密码'''
    send_report()

执行该文件:

测试邮件发送成功!

可以看到测试报告邮件已经发送成功了。打开邮箱。

成功收到了邮件。

这个demo项目就算是整体完工了。

 作为一位过来人也是希望大家少走一些弯路,希望能对你带来帮助。(WEB自动化测试、app自动化测试、接口自动化测试、持续集成、自动化测试开发、大厂面试真题、简历模板等等),相信能使你更好的进步!

留【自动化测试】即可【自动化测试交流】:574737577(备注ccc)icon-default.png?t=N5F7http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=khh1u309hOPfmutO7szune5BMv7p8BHg&authKey=kRBNmDukZpv512fdc7h2hFwQvgQFubVtbuOOFlKwsRVnal%2FcYaEiQOlTEDIi5Nd5&noverify=0&group_code=574737577

 

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

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

相关文章

B树和B+树索引

B树索引 磁盘IO读数据到内存 malloc/new 向 kernel申请4B空间&#xff0c;实际系统可能给2个页面空间即8KB&#xff0c;剩下8KB-4B空间由 lib.so 或libc.so 的 ptmalloc 或tcmalloc管理 。这样就不需要每次去kernel申请内存 磁盘IO同理&#xff0c;我们读一部分数据会把一整…

学了Python后还用学R语言吗?

学习R语言是否有必要取决于你的具体需求和背景。虽然R语言和Python都是数据科学领域广泛使用的编程语言&#xff0c;但它们之间还是存在一些差异。 如果你主要从事数据分析、统计建模或者数据可视化等工作&#xff0c;那么学习R语言可能更为适合。R语言在数据处理和统计分析方…

02_类加载子系统

02_类加载子系统 一、内存结构概述二、类加载器与类的加载过程1、类加载器子系统的作用2、类加载器ClassLoader的作用3、类的加载过程 三、类加载器的分类1、启动类加载器2、扩展类加载器3、应用程序类加载器4、自定义类加载器5、获取ClassLoader的几种方式 五、双亲委派机制1、…

浅谈.NET语言开发应用领域

.NET语言是一种跨平台的开发框架&#xff0c;适用于各种应用程序的开发。以下是一些常见的.NET语言开发应用领域&#xff1a; 桌面应用程序开发&#xff1a;使用.NET框架开发的桌面应用程序可以在Windows操作系统上运行&#xff0c;包括Windows Forms和WPF。这些应用程序可以用…

第二章 进程的描述与控制

目录 一、进程的概念、组成、特征 1.1 进程的概念 1.1.1 程序 1.1.2 进程 1.2 进程的组成 1.3 进程控制块PCB 1.4 进程的特征 二、进程的状态与转换 2.1 进程的状态 2.2 创建态、就绪态 2.3 运行态 2.4 阻塞态 2.5 终止态 2.6 进程状态的转换 2.7 进程的组织 三…

【状态估计】无迹卡尔曼滤波(UKF)应用于FitzHugh-Nagumo神经元动力学研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【ProNoC】Chap.1 ProNoC生成2x2的mesh型的4核片上网络系统;实现NoC的RTL设计

【ProNoC】Chap.1 ProNoC生成2x2的mesh型的4核片上网络系统&#xff1b;实现NoC的RTL设计 0. NoC多核片上网络生成器ProNoCProNoC的功能实现 1. 生成一个叫做Mor1kx SoC的单个Tile&#xff08;包含NI网络接口&#xff09;1.1 打开ProNoC用于生成Tile的GUI界面1.2 为Tile添加时钟…

团体程序设计天梯赛-练习集L1篇①

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的普通人。 &#x1f3c0;个人主页&#xff1a;陈童学哦CSDN &#x1f4a1;所属专栏&#xff1a;PTA &#x1f381;希望各…

【Servlet学习一】认识Servlet 创建第一个Servlet项目

目录 &#x1f31f;需要知道&#xff1a; &#x1f308;1、Tomcat是什么&#xff1f; &#x1f308; 2、Maven &#x1f31f;一、认识Servlet &#x1f308;1、Servlet是什么&#xff1f; &#x1f308;2、实现第一个Servlet项目。 &#x1f308;3、简单了解Postman工具…

LTV-6314-ASEMI代理台湾光宝高速光耦LTV-6314

编辑&#xff1a;ll LTV-6314-ASEMI代理台湾光宝高速光耦LTV-6314 型号&#xff1a;LTV-6314 品牌&#xff1a;台湾光宝 封装&#xff1a;LSOP-6 引脚数量&#xff1a;6 类型&#xff1a;光耦 特性&#xff1a;台湾光宝、IGBT驱动器、储能专用光耦&#xff3c;高速光耦 …

云服务器ECS_云主机_服务器托管_弹性计算-阿里云

阿里云服务器ECS&#xff08;Elastic Compute Service&#xff09;是一种安全可靠、弹性可伸缩的云计算服务&#xff0c;阿里云提供多种云服务器ECS实例规格&#xff0c;如通用算力型u1、ECS计算型c7、通用型g7、GPU实例等&#xff0c;阿里云服务器网分享阿里云服务器ECS详细介…

基于深度学习的高精度鸽子检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度鸽子检测识别系统可用于日常生活中或野外来检测与定位鸽子目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的鸽子目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型…

Java8 List集合如何指定打印分隔符

目录 背景方法一&#xff1a;String.join&#xff08;推荐&#xff09;方法二&#xff1a;Collectors.joining总结 背景 无论是在学习还是日常的应用开发过程中&#xff0c;我们经常会需要使用分隔符将 List 集合打印出来。 如下所示&#xff1a; import java.util.Arrays;pub…

GPT模型支持下的Python-GEE遥感云大数据分析、管理与可视化技术及多领域案例实践

随着航空、航天、近地空间等多个遥感平台的不断发展&#xff0c;近年来遥感技术突飞猛进。由此&#xff0c;遥感数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量也大幅增长&#xff0c;使其越来越具有大数据特征。对于相关研究而言&#xff0c;遥感大数据的出现为其提…

基于flask的web应用开发——搭建一个云盘

目录 0. 前言1. 实现开放下载链接2. 稍加改装3. 效果演示4. 云服务器项目部署 0. 前言 本节利用 flask 开放下载链接 操作系统&#xff1a;Windows10 家庭版 开发环境&#xff1a;Pycahrm Comunity 2022.3 Python解释器版本&#xff1a;Python3.8 第三方库&#xff1a;fla…

CSS基础学习--14 Position(定位)

一、定义 position属性指定了元素的定位类型 position 属性的五个值&#xff1a; staticrelativefixedabsolutesticky 元素可以使用的顶部&#xff0c;底部&#xff0c;左侧和右侧属性定位。然而&#xff0c;这些属性无法工作&#xff0c;除非是先设定position属性。他们也有…

leetcode90. 子集 II(java)

子集II leetcode90. 子集 II题目描述解题思路代码演示 回溯算法专题 leetcode90. 子集 II 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/subsets-ii 题目描述 给你一个整数数组 nums &#xff0c;其中可能包含重复元素…

服务器配置远程vscode

1 使用sftp同步远程代码 打开vscode&#xff0c;在扩展种搜索sftp&#xff0c;点击安装。   按住快捷键shiftctrlp&#xff0c;可以打开界面顶部的命令行&#xff0c;输入sftp&#xff0c;点击如下图的config选项&#xff1a;   会自动在.vscode目录下创建一个名为sftp.j…

Go语言实现单链表

博主最近在学习Go语言&#xff0c;所以打算更新一期Go语言版本的数据结构。这篇文章将的是Go语言如何实现单链表。 文章目录 前言一、个人见解&#xff0c;为什么学GO&#xff1f;二、Go语言实现单链表1.创建节点2.通过数组创建一个单链表3.遍历单链表4.单链表插入操作4.1 伪代…

基于AutoJs7实现的薅羊毛App专业版源码大分享

源码下载链接&#xff1a;https://pan.baidu.com/s/1QvalXeUBE3dADfpVwzF_xg?pwd0736 提取码&#xff1a;0736 专业版肯定比个人版功能强大并且要稳定。增加了很多功能的同时也测试封号的App&#xff0c;对于封号的App&#xff0c;给予剔除。虽然App数量减少了但是都是稳定的…