如何为包含许多不同类的Python程序编写配置文件



目前我有一个Python文件,其中包含类的dozon,每个类的构造函数函数中有5-10个参数。

每次当我想更改这些参数的默认值时,我都需要遍历整个文件来寻找那些构造函数,并手动修改它们,这并不太复杂,但有时会有点混乱。

我想知道有没有什么方法可以为它编写一个可读的配置文件,这样我就可以直接修改这个配置文件中的一些条目来更改相应的默认参数?我该怎么做?

谢谢!

您可以编写任何格式的配置文件。

Python提供了对JSON、XML和类似于传统分段配置文件(configparser)的格式的支持。所有这些格式都支持嵌套结构(最后一种稍微不那么有用)。

许多Python项目选择的另一个选项是用Python本身编写配置文件。事实上,Python字典看起来几乎像JSON,所以即使对于不太了解Python的人来说,它也应该很容易阅读和编写。然后可以加载并动态评估文件,这意味着您还可以使用一些奇特的东西,如检索环境变量、递归加载其他文件或使用os.path.whatever等方便的函数。如果您不想执行这种"不可信"的代码,请查看ast.literal_eval

无论您选择什么格式,都应该编写一个小的config模块,该模块提供一个函数来检索给定键的配置值。键可以是类似module.submodule.class.parameter的字符串,该字符串由函数分解为多个部分,用于在配置文件的层次结构中查找条目。最好将配置加载到内存中的数据结构中一次,然后为来自该结构的所有请求提供服务。您可以在第一次调用函数时延迟加载。

我的建议应该被视为一种意见,而不是知识(这让我怀疑这个职位有多适合SO)。

如果您已经有了一个应用程序,最简单也是我喜欢的方法是以KEY=VALUE格式的纯文本存储配置文件。在我看来,这是最简单、最干净的方法。纯文本在任何地方都是可以理解的(而不是一些qwerky格式),KEY=VALUE非常容易单独阅读和理解。

举个例子:

port=443
host=localhost
proxy=127.0.0.1

您可以将其存储在具有特定名称的当前工作目录中(.NAME格式可将其隐藏在特定操作系统上)

在这一点上,解析内容是相当直接的,这取决于应用程序的结构。(1)

with open('filename') as config_file:
    config_entries = config_file.readlines()
    for line in config_entries:
        pair = line.split('=')
        key = pair[0]
        val = pair[1]

从本质上讲,大多数元数据与代码的其余部分是解耦的,这样可以很好地保持这一点,便于将来进行维护和更新。

当然,这是一种简单化的方法,你可以选择(如@Mark所建议的)使用configparser,但我认为它增加了一层额外的复杂性,在大多数情况下你可以很容易地避免。


(1) 没有测试以下代码

关于在Python中编写配置类的最佳方式,已经有很多讨论。我更喜欢使用基于classutilities包(pip install classutilities)的类,请参阅上的文档https://pypi.org/project/classutilities/.

这个包强制您遵循PEP8(常量类变量的大写名称,无类实例和方法等),并强制类表现为"类";静态";类(在Java/C#中已知的逻辑中)。

本地&生产堆栈:

# File base_config.py
from classutilities import ConfigClassMixin, classproperty

class ConfigBase(ConfigClassMixin):
    """Base configuration options for all stacks"""
    PGSQL_HOST: str
    PGSQL_USER: str
    PGSQL_PASS: str
    PGSQL_PORT: int
    PGSQL_DATABASE: str
    CORS_ORIGINS: list[str]
    @classproperty
    def DB_CONNECTION(cls):
        """Create database connection."""
        return ...

本地配置文件可以如下所示:

# File config_local.py
from .config_base import ConfigBase

class ConfigLocal(ConfigBase):
    """Local stack configuration"""
    PGSQL_HOST: str = "database"
    PGSQL_USER: str = "whoever"
    PGSQL_PASS: str = "..."
    PGSQL_PORT: int = 5432
    PGSQL_DATABASE: str = "whatever"
    CORS_ORIGINS: list[str] = ["*"]

生产堆栈可能会使用环境变量初始化大多数变量(但从技术上讲,这是一样的)。

然后,您可以根据__init__.py文件中的环境变量(定义您想要的堆栈)轻松选择堆栈配置(考虑到整个配置构造都在配置子包中)。例如:

# File __init__.py
import os
from .config_base import ConfigBase
from .config_local import ConfigLocal
from .config_production import ConfigProduction
# Get the stack definition
stack: str = os.getenv("ENVIRONMENT", "local")
# Select correct configuration
CONFIG: type[ConfigBase] = ConfigLocal
if stack == "local":
    CONFIG: type[ConfigBase] = ConfigLocal
elif stack == "production":
    CONFIG: type[ConfigBase] = ConfigProduction
# other options here (typically 'production')
else:
    raise RuntimeError("Wrong stack name")

然后,您可以通过从子包导入变量CONFIG来使用您的配置。例如:

from whatever.config import CONFIG
# ...
# To access configuration variables/properties:
CONFIG.DB_CONNECTION.query(...)

我认为这是处理配置问题最干净的方法。

您可以在文件的开头定义一些常量,将所有默认值放在一个位置:

A_PARAM1_DEFAULT = 42
class A:
    def __init__(param1=A_PARAM1_DEFAULT):
        # do something

最新更新