python 配置文件读写

作者: orientlu | 来源:发表于2018-05-08 21:44 被阅读1900次

    @(python)

    [TOC]

    前言

    将代码中的配置项抽取到配置文件中,修改配置时不需要涉及到代码修改,避免面对一堆令人抓狂的 magic number,极大的方便后期软件的维护。

    python 本身提供标准的配置读写模块 configParse(python2,python3 修改为configparser),用于读取 ini 格式的配置文件。

    [DEFAULT]
    ServerAliveInterval = 45
    Compression = yes
    
    [topsecret.server.com]
    Port = 50022
    ForwardX11 = no
    

    除了标准库提供的模块,通过第三方模块 pyYAML, 可以读写 yaml[1]式的配置文件。

    本文介绍 python 通过 configParser 和 pyYAML 读写配置文件的方法。

    configParser

    Note The ConfigParser module has been renamed to configparser in Python 3. The 2to3 tool will automatically adapt imports when converting your sources to Python 3.

    python2 和 python3 中此模块有些许不同,旧代码可以通过工具进行转换。
    目前来说,python2 在项目中仍有使用,所以以下对在 python2 和 python3 下模块的使用都进行介绍。

    python2 - ConfigParser

    在ConfigParser 中提供了三个类:

    • RawConfigParser
    • ConfigParser
    • SafeConfigParser
      三个以此在前者的基础进行扩展,接口提供额外可选参数,提供更加复杂的功能,主要差别应该体现在对 %(value_name)s进行参数替换(value_name 为同section或者[DEFAULT]中的其他变量名才行)

    这里使用的默认配置文件 default.cfg 内容如下:

    [DEFAULT]
    default_name = lcd
    
    [Section1]
    an_int = 15
    a_bool = true
    a_float = 3.1415
    baz = fun
    bar = Python
    foo = %(bar)s is %(baz)s!
    name = s1_%(default_name)s   ; DEFAULT section's value
    

    基本读写

    使用 RawConfigParser 实现配置文件的基本的读写操作。

    import ConfigParser
    
    test_cfg = "./default.cfg"
    config_raw = ConfigParser.RawConfigParser()
    config_raw.read(test_cfg)
    
    # 读取配置文件中 [DEFAULT]
    defaults = config_raw.defaults()
    print defaults
    
    # 读取指定section下的value值
    a_float = config_raw.getfloat('Section1', 'a_float')
    print "-- number : %f type is : %s"%(a_float ,type(a_float))
    
    # 设置指定section下的value值
    # 此时没有写入文件,保存在内存实例中
    a_float = 2.14159
    config_raw.set('Section1', 'a_float', a_float)
    a_float = config_raw.getfloat('Section1', 'a_float')
    print "-- number : %f type is : %s"%(a_float ,type(a_float))
    
    # 读取带有参数替换模板的变量,但是没有替换参数
    print "-- RawConfigParser just get raw value"
    str_foo = config_raw.get('Section1', 'foo')
    print str_foo
    

    对应不同数据类型,除了调用get()获取配置文件中的原始内容,还可以使用对应的接口,getint, getboolean, 其中,布尔值 True 对应的是 1、yes、true 和 on, False 对应 0、no、false 和 off。

    运行结果

    OrderedDict([('default_name', 'lcd')])
    -- number : 3.141500 type is : <type 'float'>
    -- number : 2.141590 type is : <type 'float'>
    -- RawConfigParser just get raw value
    %(bar)s is %(baz)s!
    

    参数替换

    相比 RawConfigParser, 对于配置文件中foo = %(bar)s is %(baz)s! 这种格式的变量,在读取的时候如果想要取得替换后的值,需要使用类 ConfigParser 或者 SafeConfigParser 。
    代码如下

    config = ConfigParser.ConfigParser()
    config.read(test_cfg)
    
    print "-- ConfigParser can get interpolation"
    ## get 接口添加参数 raw,raw=1 时直接返回配置文件中的值,不做参数替换
    ## raw 默认为0,设置为0时,返回替换后的值
    str_foo = config.get('Section1', 'foo', raw=1)
    #等同 str_foo = config.get('Section1', 'foo', 1)
    print str_foo
    str_foo = config.get('Section1', 'foo')
    print str_foo
    str_foo = config.get('Section1', 'foo', 0)
    print str_foo
    
    print "-- After set a new value"
    str_foo = "%(name)s is %(baz)s!"
    config.set('Section1', 'foo', str_foo)
    str_foo = config.get('Section1', 'foo', 1)
    print str_foo
    str_foo = config.get('Section1', 'foo')
    print str_foo
    
    ## raw=0,返回替换后的值,替换的变量是在同 section 下或者 default section 中查找
    str_foo = config.get('Section1', 'name')
    print str_foo
    
    ## 接口还有另外一个可选参数 vars,
    ## 设置后,查询配置 values 时,会优先从vars这个{}寻找匹配的key返回
    ## 没有再去寻找配置文件中的。
    print "-- use default value if pass by vars={}"
    a_float = config.get('Section1', 'a_float1', vars={'a_float1':'0.01'})
    print "-- number : %f type is : %s"%(float(a_float) ,type(a_float))
    

    运行结果

    -- ConfigParser can get interpolation
    %(bar)s is %(baz)s!
    Python is fun!
    Python is fun!
    -- After set a new value
    %(name)s is %(baz)s!
    s1_lcd is fun!
    s1_lcd
    -- use default value if pass by vars={}
    -- number : 0.010000 type is : <type 'str'>
    

    使用默认参数

    有些配置参数有时候配置文件中并没有设置,此时程序中应该有对应的默认值,当找配置文件中查找不到时,使用配置值。注意和上一小节设置 vars={}不同,此处是优先返回配置文件的值,没有才返回设置的默认值,上面则相反。

    如下

    ## 设置默认值 name : default_name
    config = ConfigParser.ConfigParser({'name' : 'default_name'})
    config.readfp(open('./default.cfg'))
        
    # 读取存在的配置值,返回的是配置文件中的值
    str_foo = config.get('Section1', 'name')
    print str_foo
    print "-- use default value"
    config.remove_option('Section1', 'name')
    # 读取不存在的配置值,返回设置的默认值
    str_foo = config.get('Section1', 'name')
    print str_foo
    

    运行结果

    s1_lcd
    -- use default value
    default_name
    

    使用默认配置文件

    程序配置时,可以设置多个配置文件,并按照一定的优先级使用相应的配置文件,比如系统默认有个配置文件,不同的用户下又使用不同的配置文件,程序运行时优先使用用户配置文件中的配置参数,如果用户配置文件不存在或者对应参数没有设置,再读取系统默认配置文件中的参数值。
    此处,默认配置文件是开头提供的 default.cfg

    另新加两个配置文件:

    user1.cfg

    [DEFAULT]
    default_name = user1
    
    [Section1]
    name = s1_%(default_name)s   ; DEFAULT section's value
    

    user2.cfg

    [DEFAULT]
    default_name = user1
    
    [Section1]
    name = s1_%(default_name)s   ; DEFAULT section's value
    

    我们希望,参数使用优先级按照 user3.cfg > uer2.cfg > user1.cfg > default.cfg 使用
    实现方式如下:

    config = ConfigParser.ConfigParser()
    ## 设置默认的配置文件
    ## 注意此文件不存在会raise异常
    config.readfp(open('./default.cfg'))
    ## 设置可选配置文件,最后优先级最高,文件不存在则忽略
    config.read(['./user1.cfg', './user2.cfg', './user3.cfg'])
    # readfp 必须先调用后才能调用read,否则read中都打不开,会报错
    # config.read(['./user11111111.cfg', './user22222222.cfg'])
    
    ## 可选文件中找不到的参数,在default中查找
    an_int = config.getint('Section1', 'an_int')
    print "-- number : %f type is : %s"%(an_int ,type(an_int))
    
    ## 使用可选文件中存在的参数
    str_foo = config.get('Section1', 'name')
    print str_foo
    

    运行结果

    -- number : 15.000000 type is : <type 'int'>
    s1_user2
    

    默认文件需要存在,运行时加载失败会报错,其他可选设置文件如果找不到,直接忽略。

    python3 - configparser

    可能考虑兼容性,前面 python2 中实现的三个类在 python3 中依然支持。对于 python2 提供的参考上一节内容,接下面我们看看 python3 的使用。

    基本读写

    同 python2 差不多,加载配置文件后可以通过诸如 get, getint的接口读取参数值,也可以像读取 dict 一样读取配置参数。

    读取的配置文件example.ini 如下

    [DEFAULT]
    serveraliveinterval = 45
    compression = yes
    compressionlevel = 9
    forwardx11 = yes
    
    [bitbucket.org]
    user = hg 
    ;comment
    #comment 
    
    [topsecret.server.com]
    port = 50022
    forwardx11 = no
    

    使用接口读取上述配置文件内容

    import configparser
    
    config = configparser.ConfigParser()
    print("- Empty config %s"%config.sections())
    
    print("- Load config file")
    config.read("./example.ini")
    ## 此处返回的sections list不包括 default
    print("> config sections : %s"%config.sections()) 
    print('bitbucket.org' in config )  ## 判断配置文件中是否存在该 section
    print("> Load config file is :")
    
    for section in config.keys():
        print("[{s}]".format(s=section))
        for key in config[section]:
            print("{k} = {v}".format(k=key, v=config[section][key]))
    
    ## 如访问 dict 一样读取配置内容
    print("\n- Get value like dict :user =  %s"%config['bitbucket.org']['user'])
    conf_bitbucket = config['bitbucket.org']
    print(conf_bitbucket['user'])
    
    """
    The DEFAULT section which provides default values for all other sections"""
    print("\n- DEFAULT Section")
    ## default 是所有section的默认设置,备胎...
    for key in config['bitbucket.org']: print(key)
    print("> Get default value : forwardx11 = %s\n"%config['bitbucket.org']['forwardx11'])
    
    ## 读取不同数据类型的配置参数
    print("\n- Support datatypes")
    forwardx11 = config['bitbucket.org'].getboolean('forwardx11')
    int_port = config.getint('topsecret.server.com', 'port')
    float_port = config.getfloat('topsecret.server.com', 'port')
    print("> Get int port = %d type : %s"%(int_port, type(int_port)))
    print("> Get float port = %f type : %s"%(float_port, type(float_port)))
    

    运行结果如下:

    - Empty config []
    - Load config file
    > config sections : ['bitbucket.org', 'topsecret.server.com']
    True
    > Load config file is :
    [DEFAULT]
    serveraliveinterval = 45
    compression = yes
    compressionlevel = 9
    forwardx11 = yes
    [bitbucket.org]
    user = hg
    serveraliveinterval = 45
    compression = yes
    compressionlevel = 9
    forwardx11 = yes
    [topsecret.server.com]
    port = 50022
    forwardx11 = no
    serveraliveinterval = 45
    compression = yes
    compressionlevel = 9
    
    - Get value like dict :user =  hg
    hg
    
    - DEFAULT Section
    user
    serveraliveinterval
    compression
    compressionlevel
    forwardx11
    > Get default value : forwardx11 = yes
    
    
    - Support datatypes
    > Get int port = 50022 type : <class 'int'>
    > Get float port = 50022.000000 type : <class 'float'>
    

    默认返回

    在读取配置参数时,设置如果在配置文件中查找不到指定值,则默认返回的值。
    在指定 section 和 default 中都找不到查找的值,就会直接返回设置的 fallback, 而不是 raise 错误。

    print("\n- Return Fallback")
    print("> Get value user = %s"%(config.get('bitbucket.org', 'user')))
    print("> Get value user = %s"%(config.get('bitbucket.org', 'user', fallback="fallback_name")))
    print("> Get value forwardx11 = %s"%(config.getboolean('bitbucket.org', 'forwardx11', fallback=False)))
    print("> Get value forwardx22 = %s"%(config.getboolean('bitbucket.org', 'forwardx22', fallback=False)))
    print("> Get value user2 = %s"%(config.get('bitbucket.org', 'user2', fallback="fallback_name")))
    

    运行结果如下:

    - Return Fallback
    > Get value user = hg
    > Get value user = hg
    > Get value forwardx11 = True
    > Get value forwardx22 = False
    > Get value user2 = fallback_name
    

    参数替换

    在 python2 中提到的参数替换,pyton3 中默认使用的 BasicInterpolation(), 直接支持,在上面就提到,这种替换,变量必须是同个 section(包括default),如果要使用到其他 section 的参数,就需要使用 ExtendedInterpolation(),并且,语法上会有些许不同。

    如下,basic_interpolation.ini 是基础的替换配置文件,和 python2 一样

    [Paths]
    home_dir: /Users
    my_dir: %(home_dir)s/lumberjack
    my_pictures: %(my_dir)s/Pictures
    

    而以下这个,扩展到参数可以使用不同的 section, 在 extended_interpolation.ini 中,替换的参数写法是 ${section_name: value_name}

    [Common]
    home_dir: /Users
    library_dir: /Library
    system_dir: /System
    macports_dir: /opt/local
    
    [Frameworks]
    Python: 3.2
    path: ${Common:system_dir}/Library/Frameworks/
    
    [Arthur]
    nickname: Two Sheds
    last_name: Jackson
    my_dir: ${Common:home_dir}/twosheds
    my_pictures: ${my_dir}/Pictures
    python_dir: ${Frameworks:path}/Python/Versions/${Frameworks:Python}
    

    对以上两个配置文件进行读取,如下代码

    print("\n- BasicInterpolation")
    #default : config = configparser.ConfigParser(interpolation=configparser.BasicInterpolation())
    ## 默认使用的 interpolation 就是 BasicInterpolation()
    config.read("./basic_interpolation.ini")
    print("> Get raw value %s"%(config.get('Paths', 'my_dir', raw = 1)))
    print("> Get value %s"%(config.get('Paths', 'my_dir', raw = 0)))
    
    
    print("\n- ExtendedInterpolation - other sections")
    config = configparser.ConfigParser(interpolation=configparser.ExtendedInterpolation())
    config.read("./extended_interpolation.ini")
    print("> Get raw value %s"%(config.get('Arthur', 'python_dir', raw = 1)))
    print("> Get value %s"%(config.get('Arthur', 'python_dir', raw = 0)))
    

    运行结果 :

    - BasicInterpolation
    > Get raw value %(home_dir)s/lumberjack
    > Get value /Users/lumberjack
    
    - ExtendedInterpolation - other sections
    > Get raw value ${Frameworks:path}/Python/Versions/${Frameworks:Python}
    > Get value /System/Library/Frameworks//Python/Versions/3.2
    

    pyYAML

    jason

    参考


    1. http://www.ruanyifeng.com/blog/2016/07/yaml.html?f=tt

    相关文章

      网友评论

        本文标题:python 配置文件读写

        本文链接:https://www.haomeiwen.com/subject/koqgrftx.html