当前位置: 首页 > article >正文

YOLOv6-4.0部分代码阅读笔记-config.py

config.py

yolov6\utils\config.py

目录

config.py

1.所需的库和模块

2.class ConfigDict(Dict): 

3.class Config(object): 


1.所需的库和模块

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# The code is based on
# https://github.com/open-mmlab/mmcv/blob/master/mmcv/utils/config.py
# Copyright (c) OpenMMLab.

import os.path as osp
import shutil
import sys
import tempfile
from importlib import import_module
# 从 addict 库中导入 Dict 类的语句。 addict 是一个 Python 库,它提供了一个类似于字典的类 Dict ,但它增加了一些额外的功能,使得字典的使用更加灵活和方便。
from addict import Dict

2.class ConfigDict(Dict): 

# 这个 ConfigDict 类的目的是提供一个字典,当尝试访问不存在的键或属性时,会抛出异常而不是返回 None 或其他默认值。这在配置管理中很有用,因为它可以确保所有必要的配置项都存在,从而避免在程序运行时出现未定义配置项的问题。
#  定义了一个名为 ConfigDict 的类,它继承自 Dict 类(一个类似于字典的类 Dict )。
class ConfigDict(Dict):

    # 定义了一个特殊方法 __missing__ ,这个方法会在尝试获取字典中不存在的键时被调用。
    def __missing__(self, name):
        # 当尝试获取的键不存在时, __missing__ 方法会抛出一个 KeyError 异常,并传入不存在的键名。
        raise KeyError(name)

    # 定义了一个特殊方法 __getattr__ ,这个方法会在尝试访问对象不存在的属性时被调用。
    def __getattr__(self, name):
        # try 块,用于捕获在获取属性时可能发生的异常。
        try:
            # 尝试通过父类(即 Dict )的 __getattr__ 方法获取属性值。
            value = super(ConfigDict, self).__getattr__(name)
        # 如果在父类中没有找到对应的键,会捕获 KeyError 异常。
        except KeyError:
            # 如果捕获到 KeyError ,会创建一个 AttributeError 异常,提示用户尝试访问的属性不存在。
            # {}' 对象没有属性 '{}'
            ex = AttributeError("'{}' object has no attribute '{}'".format(
                self.__class__.__name__, name))
        # 捕获除了 KeyError 之外的其他所有异常,并将异常对象赋值给   ex  。
        except Exception as e:
            ex = e
        # 如果没有异常发生,即属性存在,返回属性值。
        else:
            return value
        # 如果捕获到异常,抛出异常对象 ex 。
        raise ex

3.class Config(object): 

# class Config(object): 定义了一个配置类,这个类用于存储和管理模型的各种配置参数。
class Config(object):

    # 这段代码定义了一个静态方法 _file2dict ,它的作用是将一个 Python 文件( .py 文件)转换成一个字典,同时生成该文件内容的文本。这个方法主要用于将配置文件转换为易于处理的格式。
    # 装饰器表示这个方法是一个静态方法,它不会接收类或实例的隐式第一个参数(即 self 或 cls )。
    @staticmethod
    # 接受一个参数 filename ,即要转换的文件名。
    def _file2dict(filename):
        # 确保 filename 参数是一个字符串。
        filename = str(filename)
        # 检查文件名是否以 .py 结尾,即是否为 Python 文件。
        if filename.endswith('.py'):
            # tempfile.TemporaryDirectory()
            # tempfile.TemporaryDirectory() 是 Python 标准库 tempfile 模块中的一个函数,它用于创建一个临时目录。这个临时目录在创建时是空的,并且在使用完毕后可以自动删除。
            # 1. 创建临时目录 : TemporaryDirectory() 函数会创建一个新的临时目录。这个目录是随机命名的,以确保不同进程或线程创建的临时目录不会冲突。
            # 2. 上下文管理器 : TemporaryDirectory() 函数返回一个上下文管理器对象。这意味着你可以用 with 语句来使用它,确保临时目录在使用后能够被正确清理。
            # 3. 自动清理 :当 with 语句块执行完毕时, TemporaryDirectory 对象的 __exit__ 方法会被调用,这会删除临时目录及其包含的所有文件和子目录。
            # 4. 参数 : TemporaryDirectory() 函数接受一些参数,允许你自定义临时目录的创建行为,例如指定父目录、设置目录的权限等。

            # 创建一个临时目录,用于存放临时文件。
            with tempfile.TemporaryDirectory() as temp_config_dir:
                # shutil.copyfile(src, dst)
                # shutil.copyfile() 是 Python 标准库 shutil 模块中的一个函数,用于将一个文件的内容复制到另一个文件。这个函数会打开源文件和目标文件,读取源文件的内容,并将其写入目标文件。
                # 参数 :
                # src : 源文件的路径。
                # dst : 目标文件的路径。如果目标文件已存在,则会被覆盖。
                # 返回值 :
                # 无返回值。
                # 异常 :
                # 如果源文件不存在或无法读取,或者目标文件无法写入,可能会抛出 FileNotFoundError 或 PermissionError 。

                # 将原始文件复制到临时目录,并重命名为 _tempconfig.py  。
                shutil.copyfile(filename,
                                osp.join(temp_config_dir, '_tempconfig.py'))
                # sys.path.insert(index, path)
                # 1.index :是要插入路径的索引位置。
                # 2.path :是要插入的路径字符串。
                # sys.path.insert 是 Python 标准库 sys 模块中的一个函数,它用于在 sys.path 列表中插入一个路径。 sys.path 是一个字符串列表,包含了解释器在搜索模块时会查找的目录。当你尝试导入一个模块时,Python 解释器会按照 sys.path 中的顺序来查找模块。
                # 插入位置 : index 参数指定了 path 被插入到 sys.path 列表中的位置。索引从 0 开始,表示列表的第一个元素。如果 index 大于当前列表的长度, path 将被追加到列表的末尾。
                # 修改 sys.path : sys.path.insert 会直接修改全局的 sys.path 列表,这意味着这个改变会影响到当前 Python 进程中所有后续的模块导入操作。
                # 使用 sys.path.insert 时需要谨慎,因为不当的修改可能会导致模块导入错误或者覆盖已有的模块路径。通常,这种修改应该在程序的初始化阶段进行,以避免在程序运行过程中意外地影响到模块的导入。

                # 将临时目录添加到系统路径的开头(索引 0),以便能够导入临时文件作为模块。
                sys.path.insert(0, temp_config_dir)
                # def import_module(name, package=None): -> 导入一个模块。
                # -> 使用 _bootstrap 模块的 _gcd_import 函数来执行实际的导入操作。 name[level:] 表示去掉模块名前的点, package 是导入的上下文包, level 是相对导入的层级。
                # -> return _bootstrap._gcd_import(name[level:], package, level)

                # 使用前面 import_module 函数导入临时文件作为模块。
                mod = import_module('_tempconfig')
                # 从系统路径中移除临时目录,避免对其他模块的导入造成影响。
                sys.path.pop(0)
                # 创建一个字典,包含模块字典中的所有非特殊属性(即不以双下划线开头的属性)。
                cfg_dict = {
                    name: value
                    for name, value in mod.__dict__.items()
                    if not name.startswith('__')
                }
                # delete imported module
                # 从 sys.modules 中删除临时模块,以释放资源。
                del sys.modules['_tempconfig']
        else:
            # 如果文件不是 .py 文件,抛出 IOError 异常,提示只支持 .py 类型的文件。
            # 目前仅支持.py 类型!
            raise IOError('Only .py type are supported now!')
        # 初始化 cfg_text 变量,包含文件名和一个换行符。
        cfg_text = filename + '\n'
        # 打开原始文件进行读取。
        with open(filename, 'r') as f:
            # 读取文件内容,并将其追加到 cfg_text 变量中。
            cfg_text += f.read()

        # 返回一个元组,包含配置字典 cfg_dict 和文件内容文本 cfg_text 。
        return cfg_dict, cfg_text

    # 这段代码定义了 Config 类的一个静态方法 fromfile ,它的作用是从文件中读取配置并创建一个 Config 类的实例。
    @staticmethod
    # 接受一个参数 filename ,即要读取配置的文件名。
    def fromfile(filename):
        # def _file2dict(filename): -> 将一个 Python 文件( .py 文件)转换成一个字典,同时生成该文件内容的文本。 -> return cfg_dict, cfg_text
        # 调用 Config 类中的私有静态方法 _file2dict ,传入 filename 参数。这个方法的作用是将文件内容转换为一个字典 cfg_dict 和一个包含文件内容的字符串 cfg_text 。
        cfg_dict, cfg_text = Config._file2dict(filename)
        # 使用从文件中读取的配置字典 cfg_dict 和文件内容 cfg_text ,以及文件名 filename 来创建一个新的 Config 类实例,并返回这个实例。
        return Config(cfg_dict, cfg_text=cfg_text, filename=filename)

    # 1.cfg_dict : 配置字典,用于存储配置项的键值对,默认为 None 。
    # 2.cfg_text : 配置文本,用于存储配置文件的原始文本内容,默认为 None 。
    # 3.filename : 配置文件名,用于指定配置文件的路径,默认为 None 。
    def __init__(self, cfg_dict=None, cfg_text=None, filename=None):
        # 如果 cfg_dict 参数为 None ,则将其设置为一个空字典。
        if cfg_dict is None:
            cfg_dict = dict()
        elif not isinstance(cfg_dict, dict):
            # 如果 cfg_dict 不是 None 但也不是字典类型,则抛出 TypeError 异常。
            # cfg_dict 必须是一个字典,但得到的是 {}。
            raise TypeError('cfg_dict must be a dict, but got {}'.format(
                type(cfg_dict)))
        
        #  __setattr__(self, name, value)
        # 1.self : 类的实例。
        # 2.name : 要设置的属性的名称。
        # 3.value : 要设置的属性的值。
        # 在 Python 中, __setattr__ 是一个特殊方法,也称为魔术方法或内置方法。当设置对象的属性时,会自动调用这个方法。如果一个类中定义了 __setattr__ 方法,那么在设置任何属性时都会触发它,而不是直接设置属性。

        # 使用 super 函数调用父类的 __setattribute__ 方法,设置实例的 _cfg_dict 属性。这里使用 ConfigDict 类来包装 cfg_dict ,以便提供额外的功能(如属性访问和类型检查)。
        # ConfigDict(cfg_dict) 创建一个 ConfigDict 实例,传入 cfg_dict 作为参数。
        super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict))
        # 使用 super 函数设置实例的 _filename 属性,存储配置文件名。
        super(Config, self).__setattr__('_filename', filename)
        # 如果 cfg_text 参数不为 None ,则直接使用它作为配置文本。
        if cfg_text:
            text = cfg_text
        # 如果 cfg_text 为 None 但 filename 不为 None ,则从文件中读取配置文本。
        elif filename:
            with open(filename, 'r') as f:
                # 读取文件内容并赋值给 text 变量。
                text = f.read()
        # 如果 cfg_text 和 filename 都为 None ,则设置空字符串作为配置文本。
        else:
            text = ''
        # 使用 super 函数设置实例的 _text 属性,存储配置文本。
        super(Config, self).__setattr__('_text', text)

    # @property
    # 在 Python 中, @property 装饰器用于将一个方法转换为属性访问器,使得可以通过属性访问的方式来调用这个方法。这种方式提供了一种更自然和直观的方式来访问和修改对象的状态。
    # 这段代码定义了一个名为 filename 的属性访问器。当你使用 obj.filename 访问这个属性时,Python 解释器会自动调用这个方法,并返回 self._filename 的值。
    # 这个属性访问器的作用是 :
    # 1. 封装性 :隐藏了 _filename 属性的实现细节,使得外部代码只能通过 filename 属性访问器来获取文件名。
    # 2. 只读属性 :由于没有提供对应的 setter 方法, filename 属性是只读的。外部代码不能直接修改 _filename 属性的值。
    # 3. 延迟计算 :可以在 filename 方法中添加额外的逻辑,例如延迟计算文件名,而不是直接存储文件名。
    # 总之, @property 装饰器提供了一种方便的方式来控制属性的访问,使得代码更加简洁和易于维护。
    @property
    def filename(self):
        return self._filename

    @property
    def text(self):
        # 表示当访问 text 属性时,这个方法将返回实例的私有属性 _text 的值。
        return self._text

    # 这个 __repr__ 方法的作用是提供一个清晰、准确的对象表示,使得当打印 Config 实例或在调试时查看实例时,可以获得关于实例的有用信息。这个方法的返回值通常包括对象的类名、关键属性和重要的内部状态。
    # 这段代码定义了 Config 类的 __repr__ 方法,这是一个特殊方法,用于返回对象的官方字符串表示形式,通常用于调试和开发。
    def __repr__(self):
        # 这行代码构建并返回一个字符串,该字符串描述了 Config 实例的官方字符串表示形式。
        # self.filename 是 Config 实例的 filename 属性,它返回配置文件的路径。
        # self._cfg_dict.__repr__() 是对 _cfg_dict 属性调用 __repr__ 方法,
        # _cfg_dict 是一个 ConfigDict 实例,它包含了配置项的键值对。
        # __repr__ 方法返回 _cfg_dict 的官方字符串表示形式,通常是一个可打印的字典表示。
        return 'Config (path: {}): {}'.format(self.filename,
                                              self._cfg_dict.__repr__())
    
    # 例:
    # 创建 Config 类的实例
    # config = Config('path/to/config.py', {'key': 'value'})
    # 打印 Config 实例
    # print(repr(config))  # 输出: Config (path: path/to/config.py): {'key': 'value'}

    # 这段代码定义了 Config 类的 __getattr__ 方法,这是一个特殊方法,用于处理访问未定义属性的情况。
    # 1.self : 类的实例。
    # 2.name : 尝试访问的属性名称。
    def __getattr__(self, name):
        # 当尝试访问 Config 实例的属性时,如果该属性在 Config 类中未定义,Python 解释器会自动调用 __getattr__ 方法。这个方法会尝试从 self._cfg_dict (一个 ConfigDict 实例)中获取名为 name 的属性。

        # getattr(object, name[, default])    
        # getattr() 是一个内置函数,用于获取对象的属性值。
        # object :必需参数,表示要从中获取属性的对象。
        # name :必需参数,表示要获取的属性的名称,应该是一个字符串。
        # default :可选参数,表示当属性不存在时返回的默认值。如果未提供此参数并且属性不存在, etattr() 函数将引发 AttributeError 异常。

        # 这个   __getattr__   方法的作用是允许 Config 类的实例通过属性访问的方式动态地访问 _cfg_dict 中的配置项。这意味着你可以使用点符号( . )来访问配置字典中的值,而不是使用字典的键访问语法( [] )。
        return getattr(self._cfg_dict, name)
    
    # 例:
    # 创建 Config 类的实例
    # config = Config({'key1': 'value1', 'key2': 'value2'})
    # 访问配置项
    # print(config.key1)  # 输出: value1
    # print(config.key2)  # 输出: value2

    # 这段代码定义了 Config 类的 __setattr__ 方法,这是一个特殊方法,用于在设置对象属性时被自动调用。
    # 1.self : 类的实例。
    # 2.name : 要设置的属性的名称。
    # 3.value : 要设置的属性的值。
    def __setattr__(self, name, value):
        # 检查 value 是否是一个字典类型。
        if isinstance(value, dict):
            # 如果 value 是一个字典,那么将其转换为 ConfigDict 类的实例。 ConfigDict 是一个扩展自 dict 的类,提供了额外的功能,比如属性访问和类型检查。
            value = ConfigDict(value)
        # 使用 self._cfg_dict (一个 ConfigDict 实例)的 __setattr__ 方法来设置属性。这里使用 __setattr__ 而不是直接赋值,是为了确保 self._cfg_dict 中的属性设置能够保持 ConfigDict 的行为,比如类型检查和特殊方法调用。

        # 这个方法的作用是允许 Config 类的实例动态地设置 _cfg_dict 中的配置项,并且如果设置的值是字典类型,会自动将其转换为 ConfigDict 实例,从而保持配置项的一致性和 ConfigDict 提供的额外功能。
        self._cfg_dict.__setattr__(name, value)

    # 例:
    # 创建 Config 类的实例
    # config = Config()
    # 动态设置配置项
    # config.key1 = 'value1'  # 设置一个字符串值
    # config.key2 = {'subkey1': 'subvalue1'}  # 设置一个字典值,将被转换为 ConfigDict
    # 访问配置项
    # print(config.key1)  # 输出: value1
    # print(config.key2)  # 输出: ConfigDict({'subkey1': 'subvalue1'})


http://www.kler.cn/a/373944.html

相关文章:

  • 【AAOS】【源码分析】CarSystemUI
  • CVE-2022-0185
  • .NET Core WebApi第4讲:控制器、路由
  • etcd 备份还原
  • vue的路由的两种模式 hash与history 详细讲解
  • 输出特殊图案,请在c环境中运行
  • 【C++】深究类型转换
  • LVGL添加事件和删除事件
  • 一年期免费HTTPS证书:网络安全新选择
  • Docker环境安装MySQL
  • ubuntu交叉编译libffi库给arm平台使用
  • Jenkins Pipeline 部署总结
  • 爬虫笔记22——当当网图书详情页静、动态数据爬取
  • leetcode day7 442
  • 6. 线程池实现
  • 如何安装和使用PowerDesigner
  • TDengine 数据订阅 vs. InfluxDB 数据订阅:谁更胜一筹?
  • ETLCloud遇上MongoDB:灵活数据流,轻松管理
  • 四、k8s快速入门之Kubernetes资源清单
  • 忘记无线网络密码的几种解决办法
  • 【GO学习笔记 go基础】编译器下载安装+Go设置代理加速+项目调试+基础语法+go.mod项目配置+接口(interface)
  • vue中el-table显示文本过长提示
  • 函数的调用
  • vue2和vue3的数据双向绑定差异整理
  • PPT制作新选择:本地部署PPTist结合内网穿透实现实时协作和远程使用
  • 【java batik_使用BATIK解析SVG生成PNG图片】