Python: 如何保存一个简单的设置/配置文件?

我不管是 JSON pickle YAML还是什么。

我见过的所有其他实现都不是向前兼容的,所以如果我有一个配置文件,在代码中添加一个新的密钥,然后加载该配置文件,它就会崩溃。

有什么简单的方法吗?

202227 次浏览

保存并加载一个字典。您将拥有任意的键、值和任意数量的键、值对。

如果您想使用 INI 文件来保存设置,可以考虑使用 Configparser,它从文本文件加载键值对,并且可以轻松地写回文件。

INI 文件的格式如下:

[Section]
key = value
key with spaces = somevalue

Python 中的配置文件

根据所需的文件格式,有几种方法可以做到这一点。

ConfigParser [ . ini 格式]

我会使用标准的 Configparser方法,除非有令人信服的理由使用不同的格式。

像这样写一个文件:

# python 2.x
# from ConfigParser import SafeConfigParser
# config = SafeConfigParser()


# python 3.x
from configparser import ConfigParser
config = ConfigParser()


config.read('config.ini')
config.add_section('main')
config.set('main', 'key1', 'value1')
config.set('main', 'key2', 'value2')
config.set('main', 'key3', 'value3')


with open('config.ini', 'w') as f:
config.write(f)

文件格式非常简单,各节用方括号标出:

[main]
key1 = value1
key2 = value2
key3 = value3

可以像下面这样从文件中提取值:

# python 2.x
# from ConfigParser import SafeConfigParser
# config = SafeConfigParser()


# python 3.x
from configparser import ConfigParser
config = ConfigParser()


config.read('config.ini')


print(config.get('main', 'key1')) # -> "value1"
print(config.get('main', 'key2')) # -> "value2"
print(config.get('main', 'key3')) # -> "value3"


# getfloat() raises an exception if the value is not a float
a_float = config.getfloat('main', 'a_float')


# getint() and getboolean() also do this for their respective types
an_int = config.getint('main', 'an_int')

JSON [ . JSON 格式]

JSON 数据可能非常复杂,并且具有高度可移植性的优点。

将数据写入文件:

import json


config = {"key1": "value1", "key2": "value2"}


with open('config1.json', 'w') as f:
json.dump(config, f)

从文件中读取数据:

import json


with open('config.json', 'r') as f:
config = json.load(f)


#edit the data
config['key3'] = 'value3'


#write it back to the file
with open('config.json', 'w') as f:
json.dump(config, f)

YAML

提供了一个基本的 YAML 示例 在这个答案中。更多细节可以在 PyYAML 网站中找到。

基本示例

该文件可以这样加载和使用:

#!/usr/bin/env python


import ConfigParser
import io


# Load the configuration file
with open("config.yml") as f:
sample_config = f.read()
config = ConfigParser.RawConfigParser(allow_no_value=True)
config.readfp(io.BytesIO(sample_config))


# List all contents
print("List all contents")
for section in config.sections():
print("Section: %s" % section)
for options in config.options(section):
print("x %s:::%s:::%s" % (options,
config.get(section, options),
str(type(options))))


# Print some contents
print("\nPrint some contents")
print(config.get('other', 'use_anonymous'))  # Just get the value
print(config.getboolean('other', 'use_anonymous'))  # You know the datatype?

输出

List all contents
Section: mysql
x host:::localhost:::<type 'str'>
x user:::root:::<type 'str'>
x passwd:::my secret password:::<type 'str'>
x db:::write-math:::<type 'str'>
Section: other
x preprocessing_queue:::["preprocessing.scale_and_center",
"preprocessing.dot_reduction",
"preprocessing.connect_lines"]:::<type 'str'>
x use_anonymous:::yes:::<type 'str'>


Print some contents
yes
True

如您所见,您可以使用易于读写的标准数据格式。Getboolean 和 getint 等方法允许您获取数据类型,而不是简单的字符串。

编写配置

import os
configfile_name = "config.yaml"


# Check if there is already a configurtion file
if not os.path.isfile(configfile_name):
# Create the configuration file as it doesn't exist yet
cfgfile = open(configfile_name, 'w')


# Add content to the file
Config = ConfigParser.ConfigParser()
Config.add_section('mysql')
Config.set('mysql', 'host', 'localhost')
Config.set('mysql', 'user', 'root')
Config.set('mysql', 'passwd', 'my secret password')
Config.set('mysql', 'db', 'write-math')
Config.add_section('other')
Config.set('other',
'preprocessing_queue',
['preprocessing.scale_and_center',
'preprocessing.dot_reduction',
'preprocessing.connect_lines'])
Config.set('other', 'use_anonymous', True)
Config.write(cfgfile)
cfgfile.close()

结果出来了

[mysql]
host = localhost
user = root
passwd = my secret password
db = write-math


[other]
preprocessing_queue = ['preprocessing.scale_and_center', 'preprocessing.dot_reduction', 'preprocessing.connect_lines']
use_anonymous = True

XML 基本示例

Python 社区似乎根本不用于配置文件。然而,解析/编写 XML 很容易,而且使用 Python 也有很多这样做的可能性。其中之一是美味汤:

from BeautifulSoup import BeautifulSoup


with open("config.xml") as f:
content = f.read()


y = BeautifulSoup(content)
print(y.mysql.host.contents[0])
for tag in y.other.preprocessing_queue:
print(tag)

Xml 可能看起来像这样

<config>
<mysql>
<host>localhost</host>
<user>root</user>
<passwd>my secret password</passwd>
<db>write-math</db>
</mysql>
<other>
<preprocessing_queue>
<li>preprocessing.scale_and_center</li>
<li>preprocessing.dot_reduction</li>
<li>preprocessing.connect_lines</li>
</preprocessing_queue>
<use_anonymous value="true" />
</other>
</config>

尝试使用 阅读设置:

from readsettings import ReadSettings
data = ReadSettings("settings.json") # Load or create any json, yml, yaml or toml file
data["name"] = "value" # Set "name" to "value"
data["name"] # Returns: "value"

尝试使用 Cfg4py:

  1. 分层设计,支持多环境,所以不要把开发设置和生产站点设置搞混了。
  2. 代码完成。Cfg4py 将您的 yaml 转换为一个 python 类,然后在您键入代码时代码完成是可用的。
  3. 更多。

免责声明: 我是这个模块的作者

对于一个简单的配置文件,我更喜欢 JSON 文件,例如 Conf.json:

{
"version": 1,
"bind": {
"address": "127.0.0.1",
"port": 8080
},
"data": {
"a": [1, 2, 3],
"b": 2.5
}
}

然后创建这个自定义 JSON 配置读取器:

import json


class Dict(dict):
"""dot.notation access to dictionary attributes"""
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__


class Config(object):
@staticmethod
def __load__(data):
if type(data) is dict:
return Config.load_dict(data)
elif type(data) is list:
return Config.load_list(data)
else:
return data


@staticmethod
def load_dict(data: dict):
result = Dict()
for key, value in data.items():
result[key] = Config.__load__(value)
return result


@staticmethod
def load_list(data: list):
result = [Config.__load__(item) for item in data]
return result


@staticmethod
def load_json(path: str):
with open(path, "r") as f:
result = Config.__load__(json.loads(f.read()))
return result

最后,使用命令加载它:

conf = Configuration.load_json('conf.json')

现在您可以使用“点”来访问您的配置。例如:

print(conf.version)
print(conf.bind.address)
print(conf.bind.port)
print(conf.data.a)
print(conf.data.b)

我也面临同样的问题,但是另外,如果配置文件不存在的话,我希望从硬编码字段读取配置变量。
我的变体:

import json


class Configurator:


def __init__(self):
# Hard coded values if config file doesn't exist
self.alpha: int = 42
self.bravo: float = 3.14
self.charlie: str = "8.8.8.8"
self.delta: list = ["Lorem", "ipsum", "dolor", "sit", "amet"]
self.echo: dict = {"Winter": "is coming"}


def read_config_file(self, config_file_name: str = "config.json"):
try:
with open(config_file_name) as conf_file:
for k, v in json.loads(conf_file.read()).items():
setattr(self, k, v)
except Exception as e:
print(f"Error was detected while reading {config_file_name}: {str(e)}. Hard coded values will be applied")


def save_config_file(self, config_file_name: str = "config.json"):
try:
conf_items = {k: v for k, v in vars(self).items() if isinstance(v, (int, float, str, list, dict))}
with open(config_file_name, "w") as conf_file:
json.dump(conf_items, conf_file, sort_keys=False, indent=2)
except Exception as e:
print(f"Error was detected while saving {config_file_name}: {str(e)}")
from configurator import Configurator


if __name__ == '__main__':
conf = Configurator()


# Read config (values from file or hard coded values if file doesn't exist)
conf.read_config_file()


# Using values from config
a = conf.alpha


# Changing values in config
conf.bravo += 1


# Save changed config to file
conf.save_config_file()

如果配置文件不存在,它将出现在第一次调用 Save _ config _ file ()之后。如果在此之后更改 config.json,则文件中的变量必须在下一次“击败”硬编码变量。

这个代码有点不正常,在使用之前先测试一下。