天天看点

c读取ini配置文件_python读取配置的若干方式

提纲:

一、背景介绍

二、方式一:argparse

三、方式二:configParse

四、方式三:dotenv

五、方式四:pydantic

六、小结

背景

对于依赖环境或者对用户开放的变量,我们通常会使用配置文件来管理。

因此对于绝大部分情况而言,我们都需要有一个读取配置文件的操作。

主要目的即将配置文件中的变量值按合适的类型方式存储到内存中。

针对这类场景,这里对几种常见配置文件读取方式进行简单汇总。

鉴于篇幅较长,所以提前进行排序,无关优劣,按个人偏好程度,仅供参考。

偏好程度:pydantic > dotenv > configParse > argparse.

argparse的简单使用

argparse,我们通常使用其作为命令行参数的解析器。

先看个argparse的使用示例:

# -*- coding: utf-8 -*-# !/usr/bin/env pythonimport astimport argparseparser = argparse.ArgumentParser(description='Arg parse module.')parser.add_argument('str_param.', type=str, help='Required parameter')parser.add_argument("--bool_param", type=ast.literal_eval, default=False)parser.add_argument('--int_param', type=int, default=711)parser.add_argument('--bool_param_two', "-b", action="store_true",                    help="True when exists, or else False")args = parser.parse_args()print(f"int_param: {args.int_param}")params = vars(args)print(params)
           

首先需要创建解析器的句柄。

然后才能添加各类参数。

参数分两大类,必填参数和可选参数,区别在于添加参数时第一个参数的开头,是否带杠。

第一个参数,代表的是变量名。

不带杠,代表必填。带两杠,代表可选参数。

带一杠,代表起别名,只能单字符,可以单独使用,也可以跟在可选参数之后。

比如示例中的“-b”。

type用于指定类型,help用于辅助性说明,default为初始默认值。

action="store_true"代表的是有则为True,否则为False。

添加完后,需要返回其命名空间。

此时的args,往往就足够了,可以直接通过点属性的方式获取属性值。

对args进行vars转换,只是为了转换成字典,从而打印,观看整体参数赋值效果。

实际使用中看情况使用。

由于argparse自带帮助参数,因此,可以直接通过-h或者--help来获得参数解释。

执行效果如下:

c读取ini配置文件_python读取配置的若干方式

第一条即简单查看其使用说明。

第二条即观察其整体参数字典。

必填参数对应取值紧跟脚本之后,可选参数位置随意。

对于起了别名的参数,使用别名和使用变量名效果一样。

那么,对于argparse的简单使用我们也就基本过了一遍。

argparse读取配置文件

因为argparse可以通过命令行获取到必要的参数。

因此,咱们可以通过命令行来指定配置文件的路径。

当然,只是指定配置文件的路径,我们还不足以解析出配置文件的内容。

所以还需要其他库的配合使用,比如使用json.loads来解析字典型参数配置文件。

给个argparse读取配置文件的示例。

先看配置文件:

{    "dev":        {            "param_one": "value_one_dev",            "param_two": 711        },    "test":        {            "param_one": "value_one_test",            "param_two": 711        },    "prod":        {            "param_one": "value_one_prod",            "param_two": 711        }}
           

这里我们直接在一个配置文件里维护多套环境的变量信息。

每个环境都只有两个参数。

然后我们再通过argparse和json来配合读取配置信息。

# -*- coding: utf-8 -*-# !/usr/bin/env pythonimport jsonimport argparsedef parse(file_path):    """Parse the file contents with json format."""    config_file = open(file_path, "r", encoding="utf-8")    config_json = json.load(config_file)    config_file.close()    return config_jsonparser = argparse.ArgumentParser(description="Arg parse module.")parser.add_argument("file_path", type=str)parser.add_argument("env", choices=["dev", "test", "prod"], type=str)args = parser.parse_args()config = parse(args.file_path)params = config[args.env]print(params)
           

这里基本上还是对argparse的使用。

只是多了一个choice的使用,其效果类似于枚举,限定输入的内容。

通过file_path指定配置文件的路径,通过env指定配置文件中获取哪组参数。

执行效果如下:

c读取ini配置文件_python读取配置的若干方式

可以看到,我们获取到了dev下对应的参数配置。

那么,argparse读取配置的过程基本就清楚了。

configparser读取配置文件

configparser,简单音译,配置解析器,顾名思义,就是为读取配置文件而做。

加上其属于python原生自带的库,所以在通常情况下,这个库被使用的频率极高。

话不多说,直接上代码。

先看一下配置文件的内容:

[one]PARAM_ONE = 1PARAM_TWO = hello[two]PARAM_ONE = 2PARAM_TWO = worldPARAM_THREE = False
           

中括号包起来的部分,我们称之为section。

其下对应该section中的变量键值对,其中,键我们可以称之为option。

不同section下可以有相同的变量名,即可以有相同的option。

然后我们使用configparser来读取配置文件的内容:

# -*- coding: utf-8 -*-# !/usr/bin/env pythonimport osfrom configparser import ConfigParserclass ConfigCenter:    """Config center."""    def __init__(self):        """Initialize the config center."""        config_file_name = "configuration.ini"        cur_dir = os.path.dirname(os.path.realpath(__file__))        config_path = os.path.join(cur_dir, config_file_name)        self.default_config = ConfigParser()        self.default_config.read(config_path)    def __getattr__(self, item: str):        """Get configuration by the attribute name intelligently.        Usage:            _        """        section, key = item.split("_", 1)        return self.default_config.get(section=section.lower(),                                       option=key.upper())    @property    def param_three(self):        """Parse the variable of param_three."""        return self.default_config.getboolean("two", "PARAM_THREE")CONFIG_CENTER = ConfigCenter()print(CONFIG_CENTER.two_param_three)print(type(CONFIG_CENTER.two_param_three))print(CONFIG_CENTER.param_three)print(type(CONFIG_CENTER.param_three))
           

这里我们简单定义了一个配置类,在类的初始化函数中进行配置文件的加载。

然后提供了两种形式的配置变量读取方式。

一种是比较通用的,对于任意类型的配置变量。

我们都可以通过_来获取其内容。

只是这里有一个点需要注意,即这种方式获取的都为字符串类型。

另外一种则属于相对定制化一些,其好处也是显而易见的。

比如可以在读取变量时直接指定类型,比如可以对参数进行简单解析后返回。

再者我们通过装饰器property使得改函数名成了该类及其实例的一个属性,因此就可以通过点属性的方式直接获取其值。

简单看一下执行结果:

c读取ini配置文件_python读取配置的若干方式

可以发现,如同上面所述。

通过get方法获取的变量值都是字符串类型。

当然,两种方式都可以获取到配置文件中的变量值,可以配合使用,也可以根据自己喜好按需使用。

dotenv读取配置文件

dotenv的设计出发点在于环境变量的设置。

dot即为点的意思,dotenv因此也可以理解为“.env”文件。

一般而言,系统中以点开头的文件都是隐藏文件。

简单看下使用示例

先看看我们配置文件".env"中的内容:

NAME = "susan"  # docString# SECOND_NAME = "lily"AGE = 18sex = maleDEBUG = True
           

这里我们定义了若干变量。

然后,可以看到,在".env"配置文件中,可以使用#来进行注释,也可以使用分号来注释。

当然,注释可以在任意位置。

有一点需要注意的是

如果#是放在中间或结尾,则需要和内容部分有至少一个空格,否则会被认为是内容的一部分。

然后我们使用dotenv来进行配置的读取

# -*- coding: utf-8 -*-# !/usr/bin/env pythonimport astimport osfrom dotenv import load_dotenvclass ConfigCenter:    """Config center."""    def __init__(self):        """Initialize the config center."""        load_dotenv()    def __getattr__(self, item: str):        """Get configuration by the attribute name intelligently."""        return os.getenv(item)    @property    def debug_param(self):        """Parse the variable of debug."""        return ast.literal_eval(os.getenv("DEBUG"))CONFIG_CENTER = ConfigCenter()print(CONFIG_CENTER.debug)print(type(CONFIG_CENTER.debug))print(CONFIG_CENTER.debug_param)print(type(CONFIG_CENTER.debug_param))print(CONFIG_CENTER.abc)
           

在类的初始化函数中我们先加载".env"文件。

load_dotenv()默认加载的为".env"文件,若不想使用隐藏文件,或者想使用其它名字的文件,则可以在该函数调用时通过dotenv_path参数进行指定配置文件的绝对路径。

这里有个小注意点:这里如果没有设置dotenv_path参数,默认加载的是当前路径下的“.env”文件,若当前路径下没有,则会去当前项目的根下找,如果当前项目的根下也没有,则无法对变量进行赋值,即都为None。

然后同样的,这里我们使用两种方式进行变量的获取。

一种是属于通用类型的变量的设置。

即通过重写__getattr__的方式,进行类与实例变量的读取操作。

由于这里我们是通过加载环境变量的方式。

因此我们读取时,也相应的是通过os获取环境变量的方式获取对应值。

需注意的是,这种方式,所有获取的内容都属于字符串类型。

另一种则同样是属于定制化内容。

好处有以下几点:

一、可以指定变量类型。

二、可以做简单的定制化操作。

三、可以显示地看出有哪些属性。

当然,因为通过property装饰器装饰,所以可以直接通过类或者实例点属性的方式进行访问。

简单看一下执行结果:

c读取ini配置文件_python读取配置的若干方式

可以看出,通过os.getenv的方式获得的变量值,是字符串类型。

对于不存在的变量,其值对应为None。

另外,如果使用dotenv的方式进行设置环境变量。

那么变量值加不加双引号,最后都会去除双引号。

如果想在加载变量的同时,修正某个变量的值。

则可以按如下方式进行加载时修正。

load_dotenv(StringIO("debug=False"), override=True)
           

那么到这里,dotenv的方式也就基本清楚了。

pydantic读取配置文件

pydantic是一个第三方库。

其出发点是为了增强数据校验以及管理设置。

其对应配置方式和dotenv有些类似。

故而默认也是直接读取的".env"文件。

直接代码演示

这里我们有三个文件:

.env对应为配置文件,用于配置我们需要的变量值。

config.py为基础配置文件,用于指定我们需要的变量及其对应类型。

settings_test.py为对外交互的配置中心。

先看同级目录下的".env"配置文件中的内容

NAME = "susan"  # docString;SECOND_NAME = "lily"AGE = 18sex = maleDEBUG = True
           

然后我们定义基础配置

# -*- coding: utf-8 -*-# !/usr/bin/env pythonfrom pydantic import BaseSettingsclass Settings(BaseSettings):    """Base settings."""    name: str = "Sun"    second_name: str = "Li"    age: int = 18    debug: bool    class Config:        """Config for settings."""        env_file = ".env"        env_file_encoding = 'utf-8'        case_sensitive = False
           

这里定义了Settings类,继承BaseSettings类。

定义了四个类属性,也就是我们后续需要使用的一些预配置环境变量内容。

可以设定基本类型及初值情况。

默认没有初始默认值的变量必须在配置文件中指定key-value。

内部类Config可以用来指定一些约束,比如

env_file 用于指定配置文件,不加路径默认为执行命令的相对路径。

相对路径受当前执行命令所在目录的影响,所以使用相对路径时需要稍加注意。

当然,这里也可以使用绝对路径,文件也可以另行指定,不一定非要".env"文件。

env_file_encoding 用于指定内容编码格式。

case_sensitive用于指定大小写敏感,默认为False,即大小写不敏感,指的是配置文件中的内容。

因此配置文件中使用所有的key都可以任意大小写使用。

env_prefix 用于指定所有配置内容的通用前缀,往往用于区分比较常用的名字。

如果此处指定了前缀,那么.env文件中则都务必要带上对应前缀。

对外交互的配置中心内容:

# -*- coding: utf-8 -*-# !/usr/bin/env pythonfrom config_test.my_pydantic import configdef get_settings(**kwargs):  """Generate the config settings."""  return config.Settings(**kwargs)settings = get_settings()print(settings.dict())print(f"age: {settings.age}, {type(settings.age)}")print(f"name: {settings.name}, {type(settings.name)}")print(f"debug: {settings.debug}, {type(settings.debug)}")
           

关于get_settings配置需求的三种可能场景

这里仅罗列通常的需求中可能出现的三种需求场景:

第一种方式是指定一个更高优先级的配置文件,且只读更高优先级的配置。

这种方式需要注意的是:

更高级的配置文件也需要完整的配置项,如果存在缺失,则会报错,并不会说缺失则取次级配置文件寻找配置项。

代码示例:

return config.Settings(_env_file="env_file_prod", _env_file_encoding="utf-8")
           

适用场景:不同环境的切换,如测试,开发,生产。

第二种方式是多配置共同work。

核心操作:即从其他配置中获取到已有的key-value键值对,

然后将**key_value_dict传到config.Settings里,此时会优先读外面的参数,相同的参数不会读配置文件,外面没有的才会读配置文件

简单示例:

return config.Settings(app_name="settings")
           

配置生效优先级:外部传入 > 配置文件取值 > 类属性默认值

适用场景:多个配置源,如一部分从apollo获取,一部分从本地配置文件中获取。

第三种方式是用且仅用Settings里指定的配置文件

这种方式需要在配置文件中,填写全Settings中所有不具备默认值的属性

否则会报内容缺失错误。

适用场景:确定单一的配置文件来源。

当然,本示例中因为并没有传入kwargs,所以相当于走的是第三种方式。

关于get_settings的表示形式

因为get_settings返回的是Settings类实例。

所以可以直接使用实例点属性的方式进行获取值。

建议使用此方式,简单便利。

由于Settings继承的BaseSettings类,而BaseSettings本身又有dict()方法,所以也可以根据所需要的参数进行dict()转换成字典的形式。

当然,这里如果使用dict()方法进行转换时,则可以根据自己需要的场景使用类如exculde_default,exclude_unset,exclude_none等方法。

简单看一下执行结果:

c读取ini配置文件_python读取配置的若干方式

可以看出,这里的变量类型都是我们想要的。

原因很简单,因为我们在类中定义对应的变量时,指定了其基本类型。

所以获取到对应的变量值时,都会自动地转成我们需要的类型。

这里需要注意的是,所以可识别的变量仅局限于类中所定义的那些。

对于不在类中定义的属性,则无法主动识别。

当然,如果自行定义__getattr__方法,则另当别论了。

小结

argparse,我们通常使用其作为命令行参数的解析器。

但是可以配合文本解析,比如json.load来读取json格式的配置文件。

configparser,配置解析器,是使用频率极高的一种配置文件读取方式。

解析的配置文件需要写section和entry。

不同section下可以有相同的option。

dotenv,主要用于环境变量的配置。

往往需要os.getenv来配合使用。

默认配置文件为".env"文件,内容均为key=value的形式,使用井号或者分号进行注释。

os.getenv获得的变量值均为字符串类型,需要自行根据需要进行转换。

pydantic,一种增加了类型校验的第三方库。

需要执行如下命令进行依赖库安装

pip install pydantic[dotenv]
           

相似于dotenv,同样是默认使用".env"配置文件,

其对应内容一样也是key=value的形式。

当然,也可以在Config中指定自己需要的文件。

相比与dotenv的需要自行进行类型转换

pydantic需要用户自行定义继承于BaseSettings的类,然后指定变量及其类型。

也可以根据需要赋予合适的默认值。

一些官方参考链接:

dotnev:https://github.com/theskumar/python-dotenv

settings:https://fastapi.tiangolo.com/advanced/settings/

pydantic: https://pydantic-docs.helpmanual.io/

更多文章请关注

c读取ini配置文件_python读取配置的若干方式

继续阅读