万字长文 - Python 日志记录器logging 百科全书 - 高级配置之 日志分层

news2024/11/15 11:38:09

在这里插入图片描述

万字长文 - Python 日志记录器logging 百科全书 - 高级配置之 日志分层

前言

在 Python 的logging模块中,它不仅提供了基础的日志功能,还拥有一系列高级配置选项来满足复杂应用的日志管理需求。

说到logging 模块的高级配置,必须提及日志分层logging.config配置日志异步操作等关键功能。它们每一项都为开发者提供了强大的调试和监控环境,对于构建可维护和高效的日志系统至关重要。

在接下来的三篇logging高级配置 文章中,我将为读者朋友们介绍 Python logging 模块中的三个高级配置的具体应用:日志分层logging.config 以及 日志异步操作,探讨它们如何优化日志处理流程,并提升应用的整体性能。

本文将首先聚焦于 logging 模块中的日志分层概念,解析其如何使开发者能够构建具有层级结构的日志记录系统,并高效地管理和过滤日志信息。

知识点📖📖

模块释义
loggingPython 的日志记录工具,标准库
logging.getLogger获取日志记录器实例

导入模块

import logging
import logging.handlers


文章脉络:

  • 点击直达:万字长文 - Python 日志记录器logging 百科全书 之 基础配置
  • 点击直达:Python 日志记录器logging 百科全书 之 日志回滚
  • 点击直达:万字长文 - Python 日志记录器logging 百科全书 之 日志过滤
  • 点击直达:万字长文 - Python 日志记录器logging 百科全书 - 高级配置之 日志分层

理解日志分层

日志分层在管理复杂应用的日志中发挥着重要作用。

作用

日志分层在复杂应用程序中提供了高度的组织性和灵活性,允许针对不同模块或组件进行细致的日志管理,从而优化调试、维护效率,并增强整体日志系统的性能和可读性。


将 Python 的 logging 模块中的日志分层类比为 Python 中的类继承(父类和子类关系),这样可以有助于理解日志记录器(logger)之间的继承和行为传播机制。

  1. 继承:在类的继承中,子类继承父类的属性和方法。类似地,在日志分层中,子记录器会继承父记录器的配置,如日志级别和关联的处理器(handlers)。

  2. 重写和自定义:就像子类可以重写或扩展父类的方法和属性,子记录器也可以有自己的日志级别和处理器,甚至可以完全覆盖父记录器的设置。

  3. 层级关系:正如类可以有多层继承关系,日志记录器也可以形成层级结构,允许复杂和细致的控制。

  4. 传播行为:在类继承中,子类的行为可以反映父类的特征,而在日志分层中,日志消息的传播(默认情况下)会从子记录器向上到父记录器,除非显式地将 propagate 属性设置为 False

需要注意的是,这只是一个比喻,实际的实现细节和概念上仍有区别。例如,类继承是面向对象编程中的一个核心概念,涉及到更广泛的编程模式,而日志分层主要是关于信息传递和处理的策略。

简单总结如下:

特性主要作用与实现方式备注
组织性为每个模块或组件设置独立的子记录器日志分层核心优势,提供清晰的日志结构
继承和覆盖子记录器继承父记录器的配置,可根据需要覆盖日志分层核心优势,提高配置灵活性
灵活性为不同模块设置不同的日志级别多日志处理器可实现,但日志分层的优势是更加结构化和直观
性能优化控制哪些消息被记录,减少不必要的日志输出多日志处理器可实现,但日志分层的优势是更加结构化和直观
问题追踪快速定位大型应用中的问题所在模块日志记录自带buff,但日志分层的优势在于它使得定位问题变得更加容易

疑惑

在学习了前面的 万字长文 - Python 日志记录器logging 百科全书 之 日志过滤 和 初步了解到日志分层的作用之后,可能会有读者朋友们有疑问:

?既然能够通过单个记录器和多处理器满足复杂日志需求的情况下,为什么还需要使用分层的日志记录器方法???

以下是我在深入了解日志分层 的作用之后的回答:

  • 单个日志记录器配合多个处理器:在这种情况下,我们可以根据日志的类型(如错误、警告、信息等)将日志定向到不同的处理器(如文件、控制台等)。这种方法在应用结构相对简单,或者当日志需求主要围绕不同类型的日志处理时很有效。
  • 使用分层多个日志记录器和处理器:这种方法允许按模块或组件分别记录日志。每个模块或组件可以有其自定义的日志级别和处理器。这种方式适用于更复杂的应用程序,其中不同部分可能有不同的日志需求。

分层日志在大型和复杂的应用程序中提供了更好的组织性和灵活性,使得对不同模块或组件的日志管理更加精细和高效。


应用场景

日志分层的应用场景主要体现在大型、多模块的应用程序和微服务架构中,以及多团队协作的开发项目里,它帮助各个模块独立地控制日志记录,简化问题追踪和调试,提高维护效率。

  1. 大型应用程序:在大型应用程序(如电子商务平台、企业级软件等)中,应用通常分为多个模块或组件。每个组件可以有自己的日志记录器,这些记录器可以根据组件的具体需求进行配置。
  2. 多团队开发:在多团队协作的项目中,每个团队可能负责应用的不同部分。日志分层允许每个团队为其负责的部分单独配置日志。
  3. 微服务架构:在微服务架构中,每个微服务可以配置自己的日志记录器。这样可以确保每个服务的日志信息都是独立和清晰的。
  4. 调试和维护:在应用程序的维护和调试过程中,可以针对出现问题的特定模块调整日志级别,从而获取更详细的日志信息。
  5. 统一基础配置:在一个复杂的应用中,各个组件可能有不同的日志需求。使用分层结构,可以在顶层记录器上定义通用的日志策略,然后根据需要为特定子记录器定制日志行为。这种方式使得日志系统更易于维护和更新。

日志分层的要点

命名规范和核心概念

以下内容来自官方文档:


getLogger() 返回对具有指定名称的记录器实例的引用(如果已提供),或者如果没有则返回 root 。名称是以句点分隔的层次结构。多次调用 getLogger()具有相同的名称将返回对同一记录器对象的引用。在分层列表中较低的记录器是列表中较高的记录器的子项。例如,给定一个名为 foo 的记录器,名称为 foo.bar 、 和 foo.bam 的记录器都是 foo 子项。

记录器具有 有效等级 的概念。如果未在记录器上显式设置级别,则使用其父记录器的级别作为其有效级别。如果父记录器没有明确的级别设置,则检查 父级。依此类推,搜索所有上级元素,直到找到明确设置的级别。根记录器始终具有明确的级别配置(默认情况下为 WARNING )。在决定是否处理事件时,记录器的有效级别用于确定事件是否传递给记录器相关的处理器。

子记录器将消息传播到与其父级记录器关联的处理器。因此,不必为应用程序使用的所有记录器定义和配置处理器。一般为顶级记录器配置处理器,再根据需要创建子记录器就足够了。(但是,你可以通过将记录器的 propagate 属性设置为 False 来关闭传播。)


我提炼如下,简要说明了关于日志分层中的日志记录器行为和层次结构的核心概念:

  • 记录器实例引用:使用getLogger()函数可以获取具有特定名称的记录器实例。如果使用相同的名称多次调用getLogger(),将返回同一个记录器对象的引用。

  • 命名规范:记录器(logger)的名称通常遵循点号分隔的层级结构,类似于 Python 包和模块的命名方式。例如,'foo.database' 表示 foo 下的 database 子模块的记录器。

  • 层级关系:记录器的层级结构通过命名来实现。在这个层级中,一个记录器可以是另一个记录器的子项。例如,如果有一个名为 'foo' 的记录器,那么 'foo.bar''foo.bar.baz' 就是它的子记录器。

  • 有效级别:如果调用 getLogger() 时不提供名称,将返回根记录器。根记录器是所有记录器的最顶层父记录器,它的默认级别为 WARNING

  • 消息传播:子记录器的日志消息会传播到其父记录器的处理器,除非设置了propagate属性为False。通常只需为顶级记录器配置处理器,子记录器可以继承这些处理器。

这种层级结构和命名规范为大型和复杂的应用程序提供了一种高效和灵活的日志管理方式。通过恰当地命名和配置记录器,可以轻松地管理应用程序的不同部分所生成的日志,确保日志信息的清晰和有序。


命名不规范的问题

在 Python 的 logging 模块中,如果顶级日志记录器的命名与子记录器的命名不遵循统一的层级结构,会出现以下几个问题:

  1. 继承失效:日志记录器之间的层级关系是通过它们的命名来确定的。如果顶级记录器和子记录器的命名不遵循统一的层级前缀,子记录器将无法正确继承顶级记录器的配置(如处理器、级别等)。这意味着我们需要为每个子记录器单独配置处理器和其他设置,增加了配置复杂性。
  2. 日志传播问题:通常,日志消息会从子记录器传播到父记录器。如果子记录器的命名不遵循正确的层级结构,这种传播可能无法发生,导致日志消息不会被预期的父记录器(和其关联的处理器)处理。
  3. 可维护性和可读性降低:在大型项目中,清晰和一致的日志记录器命名极为重要。不遵循统一的命名规范会使代码难以理解和维护,特别是在涉及多个开发者和模块的情况下。

为了避免这些问题,所以需要使用一致的层级命名约定。例如,顶级日志记录器命名为 'foo',那么子记录器应该以 'foo.' 作为前缀,如 'foo.bar''foo.baz' 等,以确保正确的继承和日志消息传播。

什么时候使用 propagate

可能会有读者朋友们有疑问:

?既然设置logger.propagate = False,那为什么还需要使用日志分层呢? 一开就使用 非日志封层的logger不是更合适吗?

其实要回答这个疑问,就应该重新审视日志分层的作用和应用场景,以及为什么即使在某些情况下禁用了传播,日志分层仍然是有价值的。

使用 logger.propagate = False 的情况,

  1. 避免重复日志记录:当在不希望某个子记录器(logger)的日志被其父记录器也处理时。例如,当已经为子记录器配置了特定的处理器(handler)并且不希望同样的日志消息再次由更高级别的记录器处理,这时设置 propagate = False 可以防止日志消息向上传播。

  2. 特定日志处理:当我们希望对某个模块或组件的日志进行特殊处理,与应用程序的其他部分区别开来。例如,我们可能有一个记录安全相关日志的记录器,我们不希望这些日志被常规的应用程序日志处理器处理。

  3. 独立日志流:在构建库或框架时,可能希望库的日志独立于使用该库的应用程序的日志系统。在这种情况下,可以为库设置一个专用的记录器,并设置 propagate = False,以防止库的日志消息污染或干扰主应用程序的日志。

  4. 性能考虑:在一些性能敏感的应用中,防止不必要的日志传播可以减少一些处理开销,特别是当有大量的日志消息和复杂的日志处理器配置时。

即使在上面的这些情况下,分层结构依然有助于维护清晰的组织架构。我们可以为特定的模块创建专用的子记录器,给予它独立的处理器和格式化器,而不必在每个模块中创建和配置新的独立记录器。


示例代码

这里是一份能用的伪代码,用于帮助读者朋友们更好的理解日志分层的具体应用。

代码:

# -*- coding: utf-8 -*-

import logging
import logging.handlers
import sys

import requests

# 全局基础配置, 日志格式化配置
formatter = logging.Formatter('%(levelname)-7s - %(asctime)s - %(name)s - %(message)s')


class RemoteLogHandler(logging.Handler):
    """自定义远程处理器"""

    def __init__(self, remote_url, logger):
        super().__init__()
        self.remote_url = remote_url
        self.error_logger = logger
        self.setFormatter(formatter)

    def emit(self, record):
        # 发送日志记录到远程服务器
        log_entry = self.format(record)  # 格式化日志记录
        try:
            response = requests.post(self.remote_url, data=log_entry)
            response.raise_for_status()
        except Exception as e:
            record.msg = f"Original message: {record.msg}, Failed to send log to remote: {str(e)}"
            print('error_logger 等级是 ', self.error_logger.level)
            self.error_logger.handle(record)


def setup_logger(*handlers, name, level=logging.INFO):
    """
    用于设置特定记录器的函数,支持多个处理器.

    Args:
        *handlers(logging.Handler): 日志处理器
        name(str): 日志记录器名称
        level(int): 日志等级

    Returns:
        日志记录器.
    """
    logger = logging.getLogger(name)
    logger.setLevel(level)
    for handler in handlers:
        handler.setFormatter(formatter)
        logger.addHandler(handler)
    return logger


def setup_file_handler(filename, level=logging.DEBUG):
    """
    setup file handler

    Args:
        filename(str): 日志文件的名称
        level(int): 日志处理器的级别, 默认为logging.DEBUG

    Returns:

    """
    file_handler = logging.FileHandler(filename=filename, delay=True)
    file_handler.setLevel(level=level)
    file_handler.setFormatter(formatter)
    return file_handler


def setup_stream_handler(stream=sys.stdout, level=logging.INFO):
    stream_handler = logging.StreamHandler(stream=stream)
    stream_handler.setLevel(level=level)
    stream_handler.setFormatter(formatter)
    return stream_handler


def create_remote_log_handler(url, logger, level=logging.INFO):
    remote_handler = RemoteLogHandler(url, logger)
    remote_handler.setLevel(level=level)
    remote_handler.setFormatter(formatter)
    return remote_handler


if __name__ == '__main__':
    # 顶层日志记录器
    # 处理器配置, 文件处理器 和 控制台处理器
    file_handler_global = setup_file_handler(filename='ecommerce_global.log')
    stream_handler_global = setup_stream_handler(stream=sys.stdout)
    global_logger = setup_logger(
        file_handler_global, stream_handler_global, name='ecommerce', level=logging.DEBUG
    )

    # 创建错误记录器和处理器
    error_file_handler = setup_file_handler(filename='error.log', level=logging.ERROR)
    error_logger = setup_logger(error_file_handler, name='error', level=logging.WARNING)

    # 实例化RemoteLogHandler
    remote_handler_global = create_remote_log_handler(
        url='http://127.0.0.1:5000/submit_log', logger=error_logger, level=logging.ERROR
    )

    # 订单处理系统记录器和处理器
    order_file_handler = setup_file_handler('orders.log', level=logging.WARNING)
    order_logger = setup_logger(
        order_file_handler, remote_handler_global, name='ecommerce.orders', level=logging.INFO
    )

    # 支付系统记录器和处理器
    payment_file_handler = setup_file_handler(filename='payments.log', level=logging.ERROR)
    payment_logger = setup_logger(
        payment_file_handler, remote_handler_global, name='ecommerce.payments', level=logging.WARNING
    )
    #
    # 测试打印日志
    global_logger.info('Global logger configured')
    order_logger.warning('Order logger configured')
    payment_logger.error('Payment logger configured')

运行效果

如果程序没有出错的话,可以看到会创建orders.log, payments.logecommerce_global.log 三份日志文件,内容分别如下所示:

  • 可以看到 ecommerce_global.log 文件,打印的日志是包含orders.logpayments.log的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用propagate

添加以下两行代码,

  • 设置子记录器的日志消息不传播到父记录器
order_logger.propagate = False
payment_logger.propagate = False

再次运行结果如下:

  • 可以看到没有重复日志记录啦。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码释义

这份代码是一个复杂的日志系统的实现,适用于需要将日志信息记录到不同位置(如文件、控制台、远程服务器)的应用程序。展示了如何在 Python 中使用 logging 模块来创建一个分层且灵活的日志处理架构。

  1. 自定义远程日志处理器 (RemoteLogHandler)

    • 定义了一个自定义的日志处理器,用于将日志消息发送到远程服务器。
    • 如果发送失败,它会使用另一个配置的日志记录器(error_logger)来处理这些日志消息。
  2. 灵活的日志配置函数

    • 提供了多个辅助函数(setup_logger, setup_file_handler, setup_stream_handler, create_remote_log_handler),使得创建和配置日志记录器和处理器更加灵活和简洁。
  3. 分层日志记录器

    • 创建了多个日志记录器,每个记录器代表应用程序的不同部分(如全局、订单处理系统、支付系统)。
    • 每个记录器可以有自己的处理器和日志级别。

日志分层的体现

  1. 不同功能的日志记录器

    • global_logger 用于全局日志记录。
    • order_logger 专门用于订单处理系统的日志。
    • payment_logger 专门用于支付系统的日志。
  2. 层级命名

    • 日志记录器的命名体现了应用程序的层级结构。例如,ecommerce.ordersecommerce.payments 表示这些记录器是 ecommerce 的子模块。
  3. 日志传播

    • 在这个例子中,如果 propagate 属性没有被设置为 False,那么日志消息会从子记录器传播到父记录器。这意味着 order_loggerpayment_logger 的日志也可能被 global_logger 所处理,除非显式地关闭了传播。

实际应用

这个日志系统适用于大型或模块化的应用程序,其中需要对不同部分的日志进行精细控制。通过这种方法,我们可以确保不同部分的日志被适当地记录和处理,同时保持日志系统的整洁和可维护性。这对于故障排查、性能监控和安全分析等方面非常有用。

总体来说,这个代码示例展示了一个结构化的日志系统,它既灵活又能够适应不同的日志需求,非常适合复杂的应用场景。

总结🎈🎈

本文详细介绍了 Python logging 模块中的日志分层功能,强调了其在构建复杂应用程序中的重要性。以下是文章的主要要点总结:

  1. 日志分层的作用与优势
    • 提高组织性:允许开发者为不同模块或组件设置独立的子记录器。
    • 继承与覆盖:子记录器可以继承父记录器的配置,同时具备自定义设置的能力。
    • 易于问题追踪:通过层级结构,方便快速定位问题所在模块。
  2. 命名规范与层级关系
    • 通过点号分隔的命名规范,确保记录器之间的层级关系清晰明确。
    • 层级结构提供了继承和消息传播的机制,简化了配置并增加了灵活性。
  3. 传播行为与性能优化
    • 默认情况下,子记录器的日志会传播到父记录器,可通过设置 propagate 属性进行控制。
    • 正确使用日志分层可以减少不必要的日志输出,从而优化性能。
  4. 应用场景
    • 日志分层特别适用于大型、多模块应用程序和微服务架构。
    • 有助于多团队协作的项目中的日志管理和维护。
  5. 实用代码示例
    • 文章通过实际代码展示了如何设置和使用分层日志记录器。
    • 包括自定义处理器和日志记录器的配置方法,增强了文章的实用性和可操作性。

总体来说,这篇文章为理解和应用 Python 的 logging 模块提供了深入的指导,特别是在构建需要细粒度日志管理的复杂应用时。文章的结构清晰,通过逐步深入的方式,使得读者朋友容易跟进和理解。

后话

本次分享到此结束,

see you~~🏹🏹

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

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

相关文章

【代码随想录】刷题笔记Day32

前言 实在不想做项目,周末和npy聊了就业的焦虑,今天多花点时间刷题!刷刷刷刷! 93. 复原 IP 地址 - 力扣(LeetCode) 分割startindex类似上一题,难点在于:判断子串合法性(0~255)、&…

C语言之sizeof 和 strlen 详细介绍

C语言之sizeof 和 strlen 文章目录 C语言之sizeof 和 strlen1. sizeof 和 strlen 的比较1.1 sizeof1.2 strlen1.3 sizeof 和 strlen 的对比 2. 练习2.1.1 一维数组2.1.2 字符数组 1. sizeof 和 strlen 的比较 1.1 sizeof sizeof是C语言中的一个关键字,计算的是变量…

国产高云FPGA:纯verilog实现视频图像缩放,提供6套Gowin工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐国产高云FPGA相关方案推荐国产高云FPGA基础教程 3、设计思路框架视频源选择OV5640摄像头配置及采集动态彩条跨时钟FIFO图像缩放模块详解设计框图代码框图2种插值算法的整合与选择 Video Frame Buffer 图像缓存DDR3 Memory Interface 4、Go…

【前端学java】语法练习-工具类的封装(13)

往期回顾: 【前端学java】JAVA开发的依赖安装与环境配置 (0)【前端学 java】java的基础语法(1)【前端学java】JAVA中的packge与import(2)【前端学java】面向对象编程基础-类的使用 &#xff08…

leetcode:504. 七进制数

一、题目: 链接: 504. 七进制数 - 力扣(LeetCode) 函数原型: char* convertToBase7(int num) 二、思路 本题要将十进制数转换为二进制数,只要将十进制num数模7再除7,直到num等于0 每次将模7的结…

国际物流社交销售玩法拆解(三):打造社交电商式分销增长

这一篇,是国际物流行业社交销售玩法最后一篇,也是国际物流企业实现业务经营新增长、打造分销增长体系的新模式。以下,我们一起来拆解这一模式具体内容吧。 #01 国际物流第二曲线:社交电商 经营增长是企业的永恒话题。在客户成本…

【数据结构】树与二叉树(廿一):树和森林的遍历——先根遍历(递归算法PreOrder、非递归算法NPO)

文章目录 5.1 树的基本概念5.1.1 树的定义5.1.2 森林的定义5.1.3 树的术语 5.2 二叉树5.3 树5.3.1 树的存储结构1. 理论基础2. 典型实例3. Father链接结构4. 儿子链表链接结构5. 左儿子右兄弟链接结构 5.3.2 获取结点的算法5.3.3 树和森林的遍历1. 先根遍历(递归&am…

【Web】Flask|Jinja2 SSTI

目录 ①[NISACTF 2022]is secret ②[HNCTF 2022 WEEK2]ez_SSTI ③[GDOUCTF 2023] ④[NCTF 2018]flask真香 ⑤[安洵杯 2020]Normal SSTI ⑥[HNCTF 2022 WEEK3]ssssti ⑦[MoeCTF 2021]地狱通讯 ①[NISACTF 2022]is secret dirsearch扫出/secret 明示get传一个secret ?…

【洛谷 P3743】kotori的设备 题解(二分答案+循环)

kotori的设备 题目背景 kotori 有 n n n 个可同时使用的设备。 题目描述 第 i i i 个设备每秒消耗 a i a_i ai​ 个单位能量。能量的使用是连续的,也就是说能量不是某时刻突然消耗的,而是匀速消耗。也就是说,对于任意实数,…

国家开放大学平时作业训练题

卷代号:1400 机器人技术及应用 参考试题 一、单项选择题(每小题3分,共45分) 1.在变径轮和变形车轮的设计中,借鉴了( )的设计,使得车轮可以主动变形进行越障。 A.滑块机构 …

王者荣耀游戏

游戏运行如下: sxt Background package sxt;import java.awt.*; //背景类 public class Background extends GameObject{public Background(GameFrame gameFrame) {super(gameFrame);}Image bg Toolkit.getDefaultToolkit().getImage("C:\\Users\\24465\\D…

使用大语言模型 LLM 做文本分析

本文主要分享 传统聚类算法 LLM与嵌入算法 嵌入算法聚类 LLM的其他用法 聚类是一种无监督机器学习技术,旨在根据相似的数据点的特征将其分组在一起。使用聚类成簇,有助于解决各种问题,例如客户细分、异常检测和文本分类等。尽管传统的聚…

Django(九、choices参数的使用、多对多表的三种创建方式、Ajax技术)

文章目录 一、choices参数choices参数的用法choices 参数用法总结 二、MVC与MTV模式1.MVC2.MTV 三、多对多的三种创建方式1.全自动创建2.纯手动创建半自动创建 四、Django与Ajax1.什么是Ajax常见的场景Ajax案例 一、choices参数 在没有用到choices参数之前,我们在D…

【Linux】指令详解(一)

目录 1. 前言2. 与指令相关的知识2.1 文件2.2 路径 3. 常见指令3.1 pwd3.2 ls3.2.1 ls -l3.2.2 ls -la 3.3 mkdir3.4 cd3.5 clear3.6 touch 1. 前言 来学习一些Linux的指令和一些相关的知识。 第一步那肯定是打开自己的xshell。 这里可以修改字体和大小。 可以使用ctrl回车全…

特殊文件(XML文件)

一&#xff0c;XML文件概括 二&#xff0c;案例 <?xml version"1.0" encoding"UTF-8" ?> <!--注释&#xff1a;以上抬头声明必须写在第一不然报错--> <users><user id"1"><uame>张无忌</uame><性别&g…

[github初学者教程] 分支管理-以及问题解决

作者&#xff1a;20岁爱吃必胜客&#xff08;坤制作人&#xff09;&#xff0c;近十年开发经验, 跨域学习者&#xff0c;目前于新西兰奥克兰大学攻读IT硕士学位。荣誉&#xff1a;阿里云博客专家认证、腾讯开发者社区优质创作者&#xff0c;在CTF省赛校赛多次取得好成绩。跨领域…

【前端学java】java 中的数组(9)

往期回顾&#xff1a; 【前端学java】JAVA开发的依赖安装与环境配置 &#xff08;0&#xff09;【前端学 java】java的基础语法&#xff08;1&#xff09;【前端学java】JAVA中的packge与import&#xff08;2&#xff09;【前端学java】面向对象编程基础-类的使用 &#xff08…

深入了解原型与原型链

1、[[Prototype]] JS中的对象有一个特殊的 [[Prototype]] 内置属性&#xff0c;其实就是对于其他对象的引用。几乎所有的对象在创建时 [[Prototype]] 属性都会被赋予一个非空的值。 var anotherObject {a:2 }; // 创建一个关联到 anotherObject 的对象 var myObject Object…

【C++】使用std::vector()函数实现矩阵的加、减、点乘、点除等运算

本文通过vector&#xff08;&#xff09;函数表示矩阵的形式&#xff0c;对 加、减、点乘、点除等运算进行编码和运行&#xff0c;相应结果如下文所述。 #include <iostream> #include <vector>using namespace std;// 矩阵加法 vector<vector<int>> …

数据结构【栈】

文章目录 数据结构 栈栈的概念与结构栈接口实现 数据结构 栈 栈的概念与结构 栈是是一种特殊的线性表&#xff0c;栈的规定是只在一端插入删除数据&#xff0c;插入删除的一端叫做栈顶&#xff0c;另一端叫栈底。根据上面的特性&#xff0c;栈的数据是后入先出 栈接口实现 栈接…