我们项目一般本地配置文件用INI格式或者json格式,还有Yaml和XML用的也比较普遍。Yaml是2001年首次发表,现在大数据和AI很多配置调用Yaml,本文只介绍最通用的跨语言跨平台配置INI和JSON。
INI格式是大家比较熟悉的文件格式,由section和key=value构成,支持备注,用英文分号(;)开头备注。一般文件命名后缀用小写的ini。大部分时候,配置文件更多的用ini,作为配置文件,它有2个优势
- 简单。两个层级,第一层级是section,第二层级是KV,JSON层级没有约束,因为可以嵌套,层级不确定
- 支持备注。配置项大部分时候会开放给用户,或者其他开发者使用,适当的备注是很重要的。根据JSON的规范,是不支持备注的,当然也可以采用新增kv备注,或者配置中有注释,读取时去配置等规避方法,但总感觉别扭。
INI配置文件层级更明确,备注方便,使用无歧义,因此应用更加广泛,但INI配置不关注配置值类型,需要解析代码自己处理。
如下是一个简单的例子
;用户配置
[user]
;姓名
name=Lily
;年龄
age=11
python的configparser库处理INI非常简单,声明一个ConfigParser对象,调用read方法直接从文件中读取配置,则可以使用config引用配置项了。 要保存到文件,使用write方法写回文件即可,不过写回文件,备注会自动清除[捂脸]。写回不做详细介绍,因为一般是人工修改配置,程序读取配置。
from configparser import ConfigParser
config_file = "/home/your_cfg.ini"
config = ConfigParser()
# 读取配置
config.read(config_file, encoding="utf-8")
# 读取调试模式参数
debug_mode = config.get("RUN", "debug_mode", fallback="False") == "true"
如上第7行,通过config对象的get方法读取配置,第一个参数是section,第二个参数是key值,fallback参数表示如果该配置在ini中没有,那返回的值是什么。
前面已经描述,从INI中读取的值没有类型,都是字符串,需要代码自己处理,那debug_mode的处理就是读取section为RUN的小节中, key为debug_mode对应的配置值,如果为true则处理为Python的bool真True,否则为否False
下面简要描述配置值和对应代码获取到的结果
- 配置为True
[RUN]
debug_mode=true
- 无配置默认False(备注掉了就是未配置了)
[RUN]
;debug_model=True
- 配置为其他值也是False
[RUN]
debug_mode=f
JSON全称叫JavaScript Object Notation(没错,看前面是github上最广泛的语言JS[赞]),它是一种轻量级的数据交换格式。一般HTTP中,前后端通过RestFUL风格接口来交互,那数据交换格式就会经常采用JSON方式,JSON不光协议轻量级,同时还能保持原有字段类型。
JSON格式比较简单,支持KV格式,花括号{}包起来,key和value用冒号:隔开,每组kv用逗号隔开;或者列表形式,用中括号[]包起来,各个值逗号隔开。KV和列表可以嵌套,例如:
{"name": "小明", "age": 10, "friends": ["小红", "小光"]}
题外话,这个JSON跟Python里面的字典比较像,但Python比较有意思是不可变的元素都能作为key
# 数字作为key
{1: "冠军", 2:"殿军", 3:"季军"}
# 元组作为key
{(1, "TOM"): {"count": 1, ...}, (5, "Lily"):{"count": 10, ...}...}
python处理JSON就更简单了。系统自动的json库即可,json.load可以直接从文件中加载JSON配置并返回,json.loads也可以从字符串中加载。返回的都是Python中的字典(dict)或者列表(list)类型。
一般为了使用方便,还会将字典作为入参,生成预先定义好的数据结果对象,例如pydantic.BaseMode对象。
如下例,第1行开始定义Menu结构体,第22行直接从文件中读取JSON配置(1个列表嵌套配置),第24行根据json.load加载出来之后遍历的字典,来赋值生成Menu对象,指定类型和结构的配置变量,方便程序其他模块代码调用。
class Menu(BaseModel):
uri: str = Field(default="", title="菜单的uri",)
name: str = Field(default="未知名称", title="菜单展示的名称")
description: Optional[str] = Field(default="描述", title="描述")
is_hide: int = Field(default=0, title="是否隐藏",)
query: KsshPramas = Field(default=None, title="是否隐藏")
def __init__(self, **kwargs):
kwargs['uri'] = str(kwargs['uri']).lower()
super().__init__(**kwargs)
class MenuList(BaseModel):
menu_list: List[Menu] = Field(default=[], title="用户可访问的菜单配置", )
current_json = "json文件路径"
with open(current_json, "r", encoding="utf8") as f:
conf_menu = MenuList(menu_list=[])
# json.load从文件中加载,并被遍历
for menu in json.load(f):
# 实例化成Menu对象
m = Menu(**menu)
if m.is_hide:
continue
conf_menu.menu_list.append(m)
INI配置感觉更面向用户,JSON配置更显臃肿复杂。根据他们各自的特点,我们实际生产环境中,INI用于环境配置等,由用户或者运维配置,而JSON配置则多应用于发布或者较少改动的软件参数配置,由较专业人员完成配置
,