Python中常用的配置文件

常见的配置文件

软件开发过程中,配置文件是经常打交道的东西。对于一些复杂的程序来说,需要使用配置文件来配置一些参数。
比如说,测试环境、预生产环境和生产环境的地址,mysql的地址,帐号,密码等信息。因为部署的环境不同,所对的应内容就不同,在这种情况下就需要根据不同的部署环境配置不同的参数。

通常,配置文件中的内容都是以 key--value 的形式存在。

  • py文件
    在Python中,字典类型,恰好是 key--value 的形式;变量在某种意义上来说,也是 key--value 的形式。所以,在Python项目中,可以采用.py文件作为配置文件
  • yaml文件
    yaml文件是比较新的一种配置文件格式,通俗易懂,在Python、Java和PHP等项目环境中均可使用。但也有个头痛的地方,就是对齐和空格要注意雨露均沾,这个有点像Python的语法,所以在Python项目中经常结合起来使用。
    常见的yaml文件后缀为 .yaml.yml
  • ini文件
    是windows的系统配置文件所采用的存储格式,统管windows的各项配置,在windows下用的比较多,在Linux下的项目中也能使用。ini文件是比较传统的配置文件,使用频率还是比较高的,但ini文件有比较严重缺点,只支持一层参数,太复杂的项目就不适用了,现在逐渐用的比较少了。
    其后缀是.ini。例如:端口配置.ini

其余的配置文件,例如conf文件、xml文件,这两种文件,在Python项目中,接触的比较少,这里就不一一举例说明了。

接着以上篇文章logging日志进行举例,通过编写配置文件,来生成不同的日志文件信息。

logger.py

import logging

class LoggerHandler(logging.Logger):

    # 初始化 Logger
    def __init__(self,
                 name='root',
                 logger_level= 'DEBUG',
                 file=None,
                 logger_format = " [%(asctime)s] %(levelname)s [%(filename)s %(funcName)s] [ line:%(lineno)d ] %(message)s"
                 ):

        # 1、设置logger收集器,继承logging.Logger
        super().__init__(name)


        # 2、设置日志收集器level级别
        self.setLevel(logger_level)

        # 5、设置 handler 格式
        fmt = logging.Formatter(logger_format)

        # 3、设置日志处理器

        # 如果传递了文件,就会输出到file文件中
        if file:
            file_handler = logging.FileHandler(file)
            # 4、设置 file_handler 级别
            file_handler.setLevel(logger_level)
            # 6、设置handler格式
            file_handler.setFormatter(fmt)
            # 7、添加handler
            self.addHandler(file_handler)

        # 默认都输出到控制台
        stream_handler = logging.StreamHandler()
        # 4、设置 stream_handler 级别
        stream_handler.setLevel(logger_level)
        # 6、设置handler格式
        stream_handler.setFormatter(fmt)
        # 7、添加handler
        self.addHandler(stream_handler)

py文件

变量方式

pyconfig.py

import time
from configfile import logger

curTime = time.strftime("%Y-%m-%d %H_%M", time.localtime())

file = "Web_Autotest_{}.log".format(curTime)


if __name__ == '__main__':
    logger = logger.LoggerHandler(file=file)
    logger.info('hello world!')

引入时间模块,设置变量为日志文件名称file,将file变量导入LoggerHandler生成了日志文件Web_Autotest_2021-09-03 10_54.log

类方式

类方式有一个好处,继承。

比如测试环境,日志级别要比较详细,需要debug级别方便定位问题,而生产环境,日志级别只需要info级别即可

pyconfig2.py

import sys
import time
from configfile import logger

curTime = time.strftime("%Y-%m-%d %H_%M", time.localtime())

class LoggerConfig:
    name = 'Web_Autotest'
    logger_level= 'INFO'
    file ="Web_Autotest_{}.log".format(curTime)

# 继承LoggerConfig类    
class ProductLoggerConfig(LoggerConfig):
    logger_level = 'DEBUG'


if __name__ == '__main__':
    # 如果系统环境是Windows,则为测试环境
    if sys.platform =='win32':
        logger = logger.LoggerHandler(name=LoggerConfig.name,logger_level=LoggerConfig.logger_level,file=LoggerConfig.file)
    # 如果系统环境是linux,则为生产环境    
    elif sys.platform =='linux':
        logger = logger.LoggerHandler(name=ProductLoggerConfig.name,logger_level=ProductLoggerConfig.logger_level,file=ProductLoggerConfig.file)
    else:
        logger = logger.LoggerHandler(name=LoggerConfig.name,logger_level=LoggerConfig.logger_level,file=LoggerConfig.file)
    logger.info('hello')
    logger.debug('world!')

如果系统环境是Windows,则会使用LoggerConfig类变量的内容,如果系统环境是linux,则会使用ProductLoggerConfig类变量的内容,ProductLoggerConfig类继承了LoggerConfig类,所以namefile都是LoggerConfig类中的变量,唯独 logger_level = 'DEBUG'

yaml文件

yaml文件基础知识

安装:

pip install pyyaml

yaml语法规则:

  • 大小写敏感
  • 使用缩进表示层级关系,缩进空格数目不重要,只要相同层级的元素左侧对齐即可(虽然编辑器中一个Tab键是4个空格,一般不会出错,但尽量用空格缩进)
  • #表示注释

yaml支持的数据结构有3种:

  • 对象:键值对的集合,又称映射(mapping)/哈希(hashes)/字典(dictionary)
  • 数组:一组按次序排列的值,又称序列(sequence)/列表(list)
  • 纯量(scalars):单个的,不可再分的值

对象:对象的一组键值,使用冒号表示(注意:编写的时候最好冒号前后各空一个空格)

animal : dog

类似于python中的字典

{animal : "dog"}

数组:一组连词线构成的行,组成一组数组

- animal
- vegetables
- meet
- people

类似于python中的列表

["animal","vegetables","meet","people"]

复合结构:对象和数组可以结合使用,形成复合结构

language:
	- Python
	- Java
	- PHP
websites:
	Python : python.org
	Java : java.com
	YAML : yaml.org

转换为python形式:

{language:["Python","Java","PHP"],websites:{"Python":"python.org","Java":"java.com","YAML":"yaml.org"}}
yaml作为配置文件

config.yaml

logger:
  name : WebAutotest
  loggerleve : INFO
  file : WebAutotest.log

或者不用加引号,都是一样的

config2.yaml

logger:
  name : "WebAutotest"
  loggerleve : "INFO"
  file : "WebAutotest.log"
python读取yaml文件

yamlconfig.py

import yaml

# 读取yaml配置,加载配置项
f = open(r'./config.yaml',encoding='utf-8')

data = yaml.load(f.read(),Loader=yaml.FullLoader)

print(data)
f.close()

结果:

{'logger': {'name': 'WebAutotest', 'loggerleve': 'INFO', 'file': 'WebAutotest.log'}}
python写入yaml文件

yamlconfig.py

import yaml


data =  {'dbtest': {'host': '192.168.134.1', 'user': 'test', 'pwd': 'test', 'port': 3306}}


f2 = open(r'./config3.yaml',encoding='utf-8',mode='w') # 如果是追加,则将'w'改为'a+'
yaml.dump(data,f2,allow_unicode=True)
f2.close()

此时,新增了一个 config3.yaml文件,写入的内容就是
config3.yaml

dbtest:
  host: 192.168.134.1
  port: 3306
  pwd: test
  user: test

不过一般情况下,对于配置文件只进行读取操作,不会用代码对配置文件进行写入操作,需要对配置文件进行修改,手动修改即可

yaml文件操作封装

封装成函数

# 读取 yaml文件
def read_yaml(file,encoding='utf-8'):
    with open(file,encoding=encoding) as f:
        return yaml.load(f.read(),Loader=yaml.FullLoader)
        
# 写入 yaml文件
def write_yaml(file,data,encoding='utf-8'):
    with open(file, encoding=encoding,mode='w') as f:
        yaml.dump(data,stream=f,allow_unicode=True)

封装成类
如果有需要,可以封装成类,推荐将 “读” 与 “写” 两个操作分开,这样不容易造成冲突

import yaml


# 读取yaml文件
class ReadYaml():
    def __init__(self, path, param=None):
        self.path = path  # 文件路径
        self.param = param  # 不传默认获取所有数据

    # 获取yaml文件中的数据
    def get_data(self,encoding='utf-8'):
        with open(self.path, encoding=encoding) as f:
            data = yaml.load(f.read(), Loader=yaml.FullLoader)
            
            # (有点多此一举,不要也行)
            if self.param == None:
                return data  # 返回所有数据
            else:
                return data.get(self.param)  # 获取键为param的值

# 写入yaml文件
class Writeyaml():
    def __init__(self, path, message):
        self.path = path
        self.message = message

    # 追加内容
    def add_data(self, encoding="utf-8"):
        with open(self.path, encoding=encoding, mode="a+") as f:
            yaml.dump(self.message, stream=f, allow_unicode=True)

    # 重写文件内容
    def change_data(self,encoding='utf-8'):
        with open(self.path, encoding=encoding, mode='w') as f:
            yaml.dump(self.message, stream=f, allow_unicode=True)


if __name__ == "__main__":
    filepath = r'./test.yaml'
    data = ReadYaml(filepath).get_data()
    # data = ReadYaml(filepath,'logger').get_data()
    print(data)
    message = {'dbtest': {'host': '192.168.134.1', 'user': 'test', 'pwd': 'test', 'port': 3306}}
    Writeyaml(path=filepath, message=message).change_data()
    Writeyaml(path=filepath, message=data).add_data()

ini文件

ini文件基础知识

ini结构:

  • 片段(section)
  • 选项(option)(相当于python字典里的key)
  • 值(value)
ini文件作为配置文件

config.ini

[student]
name = ["丽丽","lili"]
age = 	18
favor = {"sports":"football","food":"apple"}
python读取ini文件

iniconfig.py

from configparser import ConfigParser

# 初始化
config = ConfigParser()

# 读取文件
config.read(r'config.ini',encoding='utf-8')

data = config.get('student','name')
print(data)
print(type(data))

# 结果
["丽丽","lili"]
<class 'str'>

这个读取方式有点特殊,要先读取片段student,再通过读取选项name来获取里面的值 ["丽丽","lili"]

通过python读取的ini文件里的值,都是字符串类型,所以data为字符串类型,并不是列表类型,如果内容比较复杂的时候,使用python读取的ini文件里的内容,还要再进行处理, 就会比较麻烦。

所以一般情况下,会使用 yaml文件作为配置文件,与Python项目进行结合

python修改ini文件

ini文件里的内容类似于字典,获取value后,可对其进行修改

iniconfig.py

from configparser import ConfigParser

# 初始化
config = ConfigParser()

# 读取文件
config.read(r'../file/config.ini',encoding='utf-8')

# 将age修改为20
config['student']['age'] = '20'

f2 = open(r'../file/config.ini',mode='w',encoding='utf-8')
config.write(f2)

此时,config.ini 文件中,age变为了20
config.ini

[student]
name = ["丽丽","lili"]
age = 	20
favor = {"sports":"football","food":"apple"}
ini文件操作封装
  • 第一种是继承 ConfigParser
from configparser import ConfigParser

class HandleConfig(ConfigParser):

    def __init__(self, filename):
        # 调用父类的init方法
        super().__init__()
        self.filename = filename
        self.read(filename,encoding="utf8")

    def write_data(self, section, options, value):
        """写入数据的方法"""
        self.set(section, options, value)
        self.write(fp=open(self.filename, "w"))

这时,如果要获取ini文件里的值,实例化 HandleConfig 类后,依旧使用 get方法即可
修改ini文件里的值,调用 write_data 进行传参即可

conf = HandleConfig("config.ini")

# 读取片段`student`,再通过读取选项`name`来获取里面的值 `["丽丽","lili"]`
data = config.get('student','name')
  • 第二种是自己封装一个类

开关配置文件
setting.ini

[switch]
open = True

生产环境配置文件
product.ini

[api]
pre_url = http://product.com/mvc/api

[db]
host = product.com
user = product
pwd = product
port = 3306

[data]
admin_user = 17122223333
admin_pwd = 123456

[log]
[log]
file_handler = INFO
console_handler = ERROR

测试环境配置文件
test.ini

[api]
pre_url = http://test.com/mvc/api

[db]
host = test.com
user = test
pwd = test
port = 3306

[data]
admin_user = 17111112222
admin_pwd = 123456

[log]
file_handler = INFO
console_handler = DEBUG

此时,需先判定是测试环境还是生产环境

import configparser

class ReadConfig:

    def __init__(self):
        # 实例化对象
        self.config = configparser.ConfigParser()
        # 加载文件
        self.config.read('setting.ini', encoding='utf-8')  # 先加载开关的配置
        open = self.config.getboolean('switch', 'open')  # 总开关

        # 灵活切换测试环境
        if open:  # 如果是True就加载 生产环境配置文件
            self.config.read('product.ini', encoding='utf-8')  # open是True
        else:  # 如果是False 就加载 测试环境配置文件
            self.config.read('test.ini', encoding='utf-8')  # open是False

    def get(self, section, option):
        return self.config.get(section, option)

    def getboolean(self, section, option):
        return self.config.getboolean(section, option)

    def getint(self, section, option):
        return self.config.getint(section, option)

这种方式是比较“笨”的方式,但也实现了读取配置文件的功能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值