【万字长文】Python 日志记录器logging 百科全书 之 日志过滤

news2025/1/23 7:11:31

在这里插入图片描述

Python 日志记录器logging 百科全书 之 日志过滤

前言

Pythonlogging模块中,日志过滤器(Filter)用于提供更细粒度的日志控制。通过过滤器,我们可以决定哪些日志记录应该被输出,哪些应该被忽略。这对于复杂的应用程序来说尤为重要,因为它允许我们根据日志记录的特定属性(例如日志级别、日志记录者的名称或日志记录包含的消息、上下文等)来控制日志的输出。

知识点📖📖

模块释义
loggingPython 的日志记录工具,标准库
logging.Filter用于自定义日志过滤的类
import logging


文章脉络:

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

日志过滤简介

1. 疑惑和应用场景

可能读者朋友们会有疑惑,日志记录和日志过滤都是人为的操作,既要记录,又要过滤,那为什么不在一开始就只记录过滤后的信息呢,那是否构成悖论呢?

以下是一些关于为什么需要日志过滤的考虑以及适用的应用场景:

  • 动态环境: 根据信息的重要性记录不同级别(如DEBUG、INFO、ERROR)的日志,如在调试期间记录详细的信息,但在生产环境中记录更少的信息。
  • 多个处理程序:如果应用程序同时将日志发送到不同的处理程序(例如,将日志同时写入文件和发送到远程服务器),每个处理程序可能需要记录不同级别的信息。这时需要使用过滤器来控制每个处理程序的输出。
  • 隐私保护: 为了保护敏感信息的安全,过滤掉包含敏感信息的日志是必要的,以确保不会意外泄露。

2. 日志过滤的原理

这里解释日志过滤是如何工作的,包括当日志消息被发送到处理程序前,过滤器如何干预日志记录的流程。

日志过滤的原理涉及到以下关键概念:

  • 日志记录器(Logger):在Pythonlogging模块中,日志记录器是用于创建和处理日志消息的对象。每个日志记录器通常与一个特定的模块或组件相关联。
  • 日志处理程序(Handler):处理程序决定了日志消息的最终去向,例如输出到控制台、写入文件等。处理程序通常与一个或多个日志记录器关联。
  • 日志过滤器(Filter):过滤器是一个可选组件,它可以附加到处理程序上,用于决定哪些日志消息应该被处理,哪些应该被忽略。过滤器在日志消息被发送到处理程序之前起作用。
  • 过滤规则:过滤器根据一组规则或条件来过滤日志消息。这些规则可以包括日志级别、日志记录器名称、关键词等。如果日志消息满足过滤规则,它将被允许传递到处理程序;否则,它将被忽略。
  • 日志级别:日志消息通常具有不同的级别,如DEBUG、INFO、WARNING、ERRORCRITICAL。通过设置适当的过滤规则,我们可以选择记录特定级别的消息。

过滤器添加到日志记录器

  • 用于控制哪些记录器产生的消息应该被记录,记录器过滤器影响记录器级别

过滤器添加到日志处理程序

  • 用于控制哪些消息应该发送到特定处理程序,处理程序过滤器影响处理程序级别。

当日志消息被发送到处理程序时,会首先经过与之关联的过滤器。如果消息通过了过滤器的检查,它将被处理,否则将被丢弃。这样,过滤器可以在日志记录的不同阶段对消息进行筛选,以确保只有符合条件的消息被记录。

总之,日志过滤通过过滤器提供了一种强大的机制,允许开发人员有选择地记录和处理日志消息,以满足应用程序的特定需求和调试要求。

日志过滤器

logging 的日志过滤一般是基于重写 logging.Filter 类来实现自定义过滤器。

logging.Filter

步骤如下:

  1. 创建自定义过滤器类,继承 logging.Filter 且重写 filter 方法
  2. 创建日志记录器日志处理器
  3. 添加自定义过滤器类日志处理器
  4. 设置日志格式
  5. 添加处理器(Handler)到记录器(Logger
  6. 记录日志~

代码示例:

import logging


# 步骤1:创建自定义过滤器类
class MyFilter(logging.Filter):
    def filter(self, record):
        # 在这里编写过滤逻辑
        # 返回True表示允许消息通过过滤器,返回False表示不允许
        return record.levelno >= logging.WARNING  # 只允许WARNING级别及以上的消息通过


# 步骤2:创建日志记录器和日志处理器
logger = logging.getLogger("my_logger")  # 创建记录器
logger.setLevel(logging.DEBUG)  # 设置记录器的级别为DEBUG

handler = logging.StreamHandler()  # 创建处理器(这里使用了StreamHandler,将日志消息输出到控制台)
handler.setLevel(logging.DEBUG)  # 设置处理器的级别为DEBUG

# 步骤3:添加自定义过滤器到处理器
my_filter = MyFilter()
handler.addFilter(my_filter)

# 步骤4:设置日志格式
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)

# 步骤5:添加处理器到记录器
logger.addHandler(handler)

# 步骤6:记录日志
logger.debug("This is a debug message")  # 这条消息不会被过滤器通过
logger.warning("This is a warning message")  # 这条消息会被过滤器通过

# 2023-11-14 00:28:28,714 - my_logger - WARNING - This is a warning message

代码释义

上述代码演示了如何创建一个自定义过滤器类(步骤1),将它添加到一个处理器(步骤3),并将处理器添加到记录器(步骤5)。在这个示例中,自定义过滤器MyFilter允许通过级别为logging.WARNING及以上的日志消息。所以,在最后两行日志记录中,只有警告消息被记录。其他级别的消息被过滤掉。

实际开发中需要根据自己的需求定制自定义过滤器的逻辑,以满足不同的过滤条件。

日志过滤的选择

这里列举选择日志过滤的依据,如日志级别、关键字、进程&线程名、上下文、日志记录器名称等,以及如何决定何时使用哪种过滤器。

1. 日志级别

日志级别是特别常见的过滤依据,根据消息的重要性或严重性来过滤日志。常见的日志级别包括DEBUG、INFO、WARNING、ERROR、CRITICAL等。

示例代码:

class LevelFilter(logging.Filter):
    def __init__(self, level):
        self.level = level

    def filter(self, record):
        return record.levelno == self.level
    

2. 关键字

  • 目的:过滤包含特定关键字的日志。
  • 使用场景:当需要根据消息的内容特征来过滤日志时,可以使用关键字过滤器。例如,筛选包含特定错误代码或事件描述的日志消息。

示例代码:

class KeywordFilter(logging.Filter):
    def __init__(self, keyword):
        self.keyword = keyword

    def filter(self, record):
        return self.keyword in record.getMessage()

3. 进程名 & 线程名

根据线程&进程名称来过滤日志,以区分不同的应用程序实例或进程。

应用场景:

场景适用性应用场景
根据线程名用于多线程应用程序网络服务器处理不同客户端请求,每个请求在不同线程中处理。根据线程名过滤日志可轻松识别和分析各线程的活动。
根据进程名用于多进程应用程序分布式系统中,不同进程代表不同应用实例或服务节点。根据进程名过滤日志有助于区分和监控不同进程的日志。

下面的两份代码基本一致。

进程名示例代码:

  • 只记录线程名称为Process 1的日志信息
import logging
import multiprocessing


class ProcessNameFilter(logging.Filter):
    def __init__(self, process_name):
        super().__init__()
        self.process_name = process_name

    def filter(self, record):
        return record.processName == self.process_name


# 创建日志记录器
logger = logging.getLogger("my_logger")
logger.setLevel(logging.DEBUG)

# 创建处理器(这里使用了StreamHandler,将日志消息输出到控制台)
handler = logging.StreamHandler()

# 设置日志格式
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)

# 添加处理器到记录器
logger.addHandler(handler)

# 添加自定义过滤器到处理器
thread_filter = ProcessNameFilter('Process 1')
handler.addFilter(thread_filter)


def worker_function():
    # 获取当前进程的名称
    current_process_name = multiprocessing.current_process().name

    # 记录日志
    logger.debug(f"This is a debug message {current_process_name}")
    logger.info(f"This is an info message {current_process_name}")
    logger.warning(f"This is a warning message {current_process_name}")


if __name__ == "__main__":
    process_tasks = [multiprocessing.Process(target=worker_function, name=f'Process {i}') for i in range(5)]
    [process.start() for process in process_tasks]
    [process.join() for process in process_tasks]

线程名示例代码:

  • 只记录线程名称为Thread 1的日志信息
import logging
import threading


class ThreadNameFilter(logging.Filter):
    def __init__(self, thread_name):
        super().__init__()
        self.thread_name = thread_name

    def filter(self, record):
        return record.threadName == self.thread_name


# 创建日志记录器
logger = logging.getLogger("my_logger")
logger.setLevel(logging.DEBUG)

# 创建处理器(这里使用了StreamHandler,将日志消息输出到控制台)
handler = logging.StreamHandler()

# 设置日志格式
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)

# 添加处理器到记录器
logger.addHandler(handler)

# 添加自定义过滤器到处理器
thread_filter = ThreadNameFilter('Thread 1')
handler.addFilter(thread_filter)


def worker_function():
    # 获取当前线程的名称
    current_thread_name = threading.current_thread().name

    # 记录日志
    logger.debug(f"This is a debug message {current_thread_name}")
    logger.info(f"This is an info message {current_thread_name}")
    logger.warning(f"This is a warning message {current_thread_name}")


if __name__ == '__main__':
    thread_tasks = [threading.Thread(target=worker_function, name=f'Thread {i}') for i in range(5)]
    [thread.start() for thread in thread_tasks]
    [thread.join() for thread in thread_tasks]

4. 上下文

在日志记录中,上下文信息是一种关键的元素,它允许我们更精细地控制哪些日志消息应该被记录和处理。上下文信息可以是与日志消息相关的任何附加信息,常见的用例包括:

用例目的示例
用户会话在多用户系统中,根据不同用户的会话来查看或调试日志。为每个用户的会话创建一个唯一标识符(如用户ID或会话ID),并将其添加到日志消息中。然后,使用过滤器根据特定用户的标识符来过滤日志消息。
应用状态根据应用的当前状态(如“启动中”、“运行中”、“关闭中”)来过滤日志。将应用状态信息(如状态名称)添加到日志消息中,并使用过滤器根据状态来筛选日志。这可以帮助我们了解应用在不同状态下的行为。
业务逻辑对于复杂的业务流程,可能需要根据特定的业务逻辑或执行路径来筛选日志。在记录日志时,根据执行的特定业务逻辑或步骤添加标识符或关键字,并使用过滤器根据这些标识符来过滤日志。这有助于跟踪和分析特定业务逻辑的执行情况。

下面使用一个简单的案例来展示日志过滤中上下文 的应用。

示例代码:

  • 下面代码演示了如何使用自定义过滤器和额外的上下文信息来过滤日志消息。
import logging


class ContextFilter(logging.Filter):
    def __init__(self, context):
        super().__init__()
        self.context: dict = context

    def filter(self, record):
        return getattr(record, 'user_id', None) == self.context.get('user_id')


# 创建日志记录器
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# 创建处理器(这里使用了StreamHandler,将日志消息输出到控制台)
handler = logging.StreamHandler()

# 设置日志格式
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s - user_id:%(user_id)s')
handler.setFormatter(formatter)

# 添加处理器到记录器
logger.addHandler(handler)

# 添加自定义过滤器到处理器
# 创建上下文信息
context_info = {'user_id': 'user123', 'request_id': 'abcdef'}
thread_filter = ContextFilter(context_info)
handler.addFilter(thread_filter)

# 记录日志
# 这个日志将被显示,因为它匹配了上下文过滤器
logger.debug('This is a debug message', extra={'user_id': 'user123'})
# 这个日志将不会被显示
logger.debug('This log is from another user', extra={'username': 'user456'})

代码释义:

在上面的示例中,ContextFilter 过滤器根据user_id来过滤日志,只有当日志消息中的user_id与上下文信息中的匹配时,日志消息才会被记录。这种方法允许根据自定义上下文信息轻松地过滤和分析日志消息。


5. 日志记录名称

  • 目的:根据生成它们的日志记录器的名称来过滤日志。
  • 使用场景:为不同的模块或子系统创建不同的记录器,并为每个记录器添加不同的过滤器,以根据记录器的名称来过滤消息。

示例代码:

class LoggerNameFilter(logging.Filter):
    def __init__(self, logger_name):
        self.logger_name = logger_name

    def filter(self, record):
        return record.name == self.logger_name

6. 应用场景

简单总结一下关于日志过滤的几种选择依据的应用场景。

过滤依据应用场景描述
日志级别- 调试、信息记录、警告、错误、严重错误级别的日志根据消息的重要性或严重性来筛选日志,适用于大多数日志分析场景。
关键字- 根据特定内容特征过滤日志
- 筛选包含特定错误代码或事件描述的日志
当需要根据消息的具体内容来过滤日志时使用,如筛选包含特定代码或描述的消息。
进程&线程名- 区分不同的应用程序实例或进程
- 用于多线程或多进程应用程序
适用于需要根据进程或线程来区分日志的场景,例如网络服务器或分布式系统中。
上下文- 多用户系统中按用户会话过滤
- 根据应用状态或业务逻辑过滤
使用上下文信息(如用户会话ID、应用状态)过滤日志,适用于需要按上下文细节来筛选日志的复杂应用场景。
日志记录器名称- 根据不同模块或子系统的日志记录器名称过滤为不同的模块或子系统创建不同的记录器,并根据记录器名称过滤日志,适用于模块化或分层设计的应用程序。

示例代码

这份代码是一个多功能的日志配置示例,适用于需要精确控制日志输出的应用程序。它适合于那些需要在不同环境(如开发和生产环境)下进行不同日志处理的场景。

代码

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


import logging


# 自定义过滤器 - 控制台使用
class ConsoleFilter(logging.Filter):
    def filter(self, record):
        # 过滤掉包含敏感信息的日志
        return "敏感信息" not in record.getMessage()


# 自定义过滤器 - 文件处理器使用
class FileFilter(logging.Filter):
    def filter(self, record):
        # 过滤掉上下文不匹配的日志
        if hasattr(record, 'context_id') and record.context_id != 'expected_context':
            return False
        return True


def setup_logger(name, log_file, level=logging.DEBUG):
    """配置日志记录器、处理器和过滤器"""
    _logger = logging.getLogger(name=name)
    _logger.setLevel(level=level)

    # 控制台处理器
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.DEBUG)
    console_handler.addFilter(ConsoleFilter())

    # 文件处理器
    file_handler = logging.FileHandler(filename=log_file, encoding='utf-8', delay=True)
    file_handler.setLevel(logging.INFO)
    file_handler.addFilter(FileFilter())

    # 设置日志格式
    formatter = logging.Formatter("%(levelname)-7s - %(asctime)s - %(name)s -  %(message)s")
    console_handler.setFormatter(formatter)
    file_handler.setFormatter(formatter)

    # 添加处理器到记录器
    _logger.addHandler(console_handler)
    _logger.addHandler(file_handler)

    return _logger


if __name__ == '__main__':
    # 设置日志记录器
    logger = setup_logger("app_logger", "app.log")

    # 记录日志,包含上下文信息
    logger.debug("这是一条调试消息", extra={'context_id': 'expected_context'})  # 控制台打印, 日志不记录
    logger.info("这条消息包含敏感信息:123456", extra={'context_id': 'expected_context'})  # 控制台不打印, 日志记录
    logger.info("这是一条普通消息", extra={'context_id': 'expected_context'})  # 控制台打印, 日志记录
    logger.warning("这是一条警告消息", extra={'context_id': 'unexpected_context'})  # 控制台打印, 日志不记录
    logger.error("这是一条错误消息!", extra={'context_id': 'expected_context'})  # 控制台打印, 日志记录


代码释义

  1. 自定义过滤器
    • ConsoleFilter:用于控制台处理器,过滤掉包含“敏感信息”的日志。
    • FileFilter:用于文件处理器,过滤掉上下文标识符(context_id)与"expected_context"不匹配的日志。
  2. 日志记录器配置(setup_logger 函数)
    • 配置一个名为 name 的日志记录器。
    • 设置两个处理器:控制台处理器(console_handler)和文件处理器(file_handler)。
    • 控制台处理器设置为 DEBUG 级别,文件处理器设置为 INFO 级别。
    • 为处理器添加对应的过滤器和格式器。
  3. 日志记录操作
    • 使用 logger 记录不同级别和内容的日志。
    • 根据过滤器的设置,不同的日志消息会被不同地处理:
      • 包含敏感信息的日志不会在控制台打印,但会记录到文件中。
      • 上下文不匹配的日志会在控制台打印,但不会记录到文件中。
      • 其他日志根据其级别既会打印到控制台也会记录到文件中。

代码运行结果如下:

在这里插入图片描述

如果没出错的话,日志文件中会显示如下:

在这里插入图片描述

总结🚀🚀

在本篇文章中,我们深入探讨了logging模块中的日志过滤功能,展示了其在大型和复杂应用程序中的关键作用。日志过滤器提供了精细的控制机制,允许开发者基于特定条件(如日志级别、关键字、进程&线程名、上下文及日志记录器名称)筛选和处理日志记录。

有效地使用日志过滤器不仅帮助开发者更好地调试和监控应用程序,而且还有助于保护敏感信息,提高应用性能和可维护性。

后话

本次分享到此结束,

see you~😎😎

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

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

相关文章

【开发工具】gitee还不用会?我直接拿捏 >_>

🌈键盘敲烂,年薪30万🌈 目录 git的一些前置操作 如何获取本地仓库 本地仓库的操作 远程仓库操作 合并两个仓库(通用方法) 从远程仓库拉取文件报错 fatal:refusing to merge unrelated histories 分支操作 注意&…

MHA实验和架构

什么是MHA? masterhight availabulity:基于主库的高可用环境下可以实现主从复制、故障切换 MHA的主从架构最少要一主两从 MHA的出现是为了解决MySQL的单点故障问题。一旦主库崩溃,MHA可以在0-30秒内自动完成故障切换。 MHA的数据流向和工…

QT windows与linux之间sokcet通信中文乱码问题解决方法

QT windows与linux之间sokcet通信中文乱码问题解决方法 linux发送与接收都转码utf-8: tcpClient ->write( send_msg.toUtf8());//解决乱码,发送转码 接收: QByteArray buffer tcpClient->readAll(); if(!buffer.isEmpty()) { // ui->plain…

[工业自动化-21]:西门子S7-15xxx编程 - 软件编程 - 如何快速看懂PLC梯形图?

目录 预备:电气图 1. 电路图 2. 电气图 一、梯形图概述 1.1 什么是梯形图 1.2 梯形图的作用 二、梯形图中的主要元素 三、梯形图的程序执行 3.1 梯形图扫描的原则 3.2 梯形图执行顺序 3.3 梯形图扫描 预备:电气图 1. 电路图 电路组成&#x…

雷达测角原理、测角精度、测角分辨率以及3DFFT角度估计算法汇总

1.角度测量方法 依据:电磁波的直线传播和雷达天线的方向性。 分类:振幅法测角、相位法测角 1.1 相位法测角 相位法测角利用多个天线所接收回波信号之间的相位差进行测角。如下图所示; 图 1 设在θ方向有一远区目标,则到达接收点…

【STM32】串口和printf

1.数据通信的基本知识 1.串行/并行通信 2.单工/半双工/全双工通信 类似于【广播 对讲 电话】 不是有两根线就是全双工,而是输入和输出都有对应的数据线。 3.同步/异步通信 区分同步/异步通信的根本:判断是否有时钟信号(时钟线)。…

MVC使用的设计模式

MVC使用的设计模式 一、背景 MVC模式是"Model-View-Controller"的缩写,中文翻译为"模式-视图-控制器"。MVC应用程序总是由这三个部分组成。Event(事件)导致Controller改变Model或View,或者同时改变两者。只要Controller改变了Model…

关于 Java NIO 的 Selector 的事儿,这篇文章里面全都有

前面 4 篇文章深入分析了 NIO 三大组件中的两个:Buffer 和 Channel: 【死磕 NIO】— 深入分析Buffer【死磕 NIO】— 深入分析Channel和FileChannel【死磕NIO】— 跨进程文件锁:FileLock【死磕NIO】— 探索 SocketChannel 的核心原理 这篇文…

ffmpeg5及以上-s和像素格式转换 画屏问题

环境: lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.10 Release: 22.10 Codename: kinetic拉下ffmpeg源码,6.0.1,4.3.6,5.1.4,依次安装作实验 ./configure --disable-x86asm …

msvcp140.dll丢失的解决方法、详细解析dll缺失原因及对电脑的影响

msvcp140.dll是一款Visual C Redistributable for Visual Studio 2015的运行时库,许多程序都需要依赖这个库才能正常运行。当msvcp140.dll丢失时,我们可能会遇到无法打开程序或游戏,甚至系统崩溃的问题。本文将详细介绍msvcp140.dll丢失的解决…

Linux--makefile

一、makefile的作用 makefile是一个文件,是围绕依赖关系和依赖方法的自动化编译工具 一个工程中的源文件有很多,按照不同的类型、功能、模块放在不同的目录中。而makefile定义了一系列的规则来指定,那些文件需要先编译,那些文件…

后端接口性能优化分析-程序结构优化

👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家📕系列专栏:Spring源码、JUC源码🔥如果感觉博主的文章还不错的话,请👍三连支持&…

【开源】基于JAVA的电子元器件管理系统

目录 一、摘要1.1 项目简介1.2 项目详细录屏 二、研究内容三、界面展示3.1 登录&注册&主页3.2 元器件单位模块3.3 元器件仓库模块3.4 元器件供应商模块3.5 元器件品类模块3.6 元器件明细模块3.7 元器件类型模块3.8 元器件采购模块3.9 元器件领用模块3.10 系统基础模块 …

PlantUML基础使用教程

环境搭建 IDEA插件下载 打开IEDA系列IDE,从FIle–>Settings–>Plugins–>Marketplace 进入到插件下载界面,搜索PlantUML,安装PlantUML Integration和PlantUML Parser两个插件,并重启IDE 安装和配置Graphviz 进入官网…

【Python 千题 —— 基础篇】欢迎光临

题目描述 题目描述 欢迎光临。为列表中的每个嘉宾打印欢迎光临语句。例如,有一份嘉宾列表 ["李二狗", "王子鸣"],则需要根据嘉宾名单打印输出: 欢迎光临!李二狗。 欢迎光临!王子鸣。下面是一份…

基于布谷鸟算法优化概率神经网络PNN的分类预测 - 附代码

基于布谷鸟算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于布谷鸟算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于布谷鸟优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要:针对PNN神经网络…

编程怎么学习视频教程,编程实例入门教程,中文编程开发语言工具下载

编程怎么学习视频教程,编程实例入门教程,中文编程开发语言工具下载。 给大家分享一款中文编程工具,零基础轻松学编程,不需英语基础,编程工具可下载。 这款工具不但可以连接部分硬件,而且可以开发大型的软件…

csapp第三章读书笔记

caspp chapter 3 寄存器 operand form data movement instructions mov 指令例子: 0扩展 movz 指令: Zero-extending data movement instructions是一种计算机指令类型,涉及将数据从一个位置移动到另一个位置,同时通过在最重要的一端添加零位来将数据扩…

【考研复习】二叉树的特殊存储|三叉链表存储二叉树、一维数组存储二叉树、线索二叉树

文章目录 三叉链表存储二叉树三叉链表的前序遍历(不使用栈)法一三叉链表的前序遍历(不使用栈)法二 一维数组存储二叉树一维数组存储二叉树的先序遍历 线索二叉树的建立真题演练 三叉链表存储二叉树 三叉链表结构体表示如下图所示…

探秘Vue组件间通信:详解各种方式助你实现目标轻松搞定!

🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! ​ 目录 ⭐ 专栏简介 📘 文章引言 一…