簡體   English   中英

Python:你會如何保存一個簡單的設置/配置文件?

[英]Python: How would you save a simple settings/config file?

我不在乎它是JSONpickleYAML還是其他什么。

我見過的所有其他實現都不是向前兼容的,所以如果我有一個配置文件,在代碼中添加一個新密鑰,然后加載該配置文件,它就會崩潰。

有什么簡單的方法可以做到這一點?

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 網站上找到更多詳細信息。

ConfigParser 基本示例

可以像這樣加載和使用該文件:

#!/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 有很多可能性。 一個是 BeautifulSoup:

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)

config.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>

如果您想使用 INI 文件之類的東西來保存設置,請考慮使用configparser ,它從文本文件加載鍵值對,並且可以輕松地寫回文件。

INI 文件格式為:

[Section]
key = value
key with spaces = somevalue

保存並加載字典。 您將擁有任意鍵、值和任意數量的鍵、值對。

嘗試使用ReadSettings

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"

對於簡單的配置文件,我更喜歡 JSON 文件,例如conf.json

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

然后使用 JSON 庫加載它:

import json
with open('conf.json', 'r') as f:
  confjson = Configuration(json.loads(f.read()))

最后,將其轉換為這個自定義字典類:

class Configuration(dict):
    def __init__(self, *args):
        super(Configuration, self).__init__()

        for arg in args:
            for key, value in arg.items():
                value = Configuration(value) if isinstance(value, dict) else value
                self.__setattr__(key, value)

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Configuration, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Configuration, self).__delitem__(key)
        del self.__dict__[key]

conf = Configuration(confjson)

現在您可以使用點“.”訪問您的配置。 例如:

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

我遇到了同樣的問題,但另外我想從硬編碼字段中讀取配置變量,以防配置文件不存在。
我的變體:

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()

如果配置文件不存在,它會在第一次調用conf.save_config_file()后出現。 如果之后更改 config.json,文件中的變量必須在下一次“擊敗”硬編碼變量。

代碼有點hacky,使用前測試一下。

嘗試使用cfg4py

  1. 分層設計,支持多種環境,因此永遠不要將開發設置與生產站點設置混淆。
  2. 代碼完成。 Cfg4py 會將您的 yaml 轉換為 python 類,然后在您鍵入代碼時可以使用代碼完成功能。
  3. 還有很多..

免責聲明:我是這個模塊的作者

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM