前言
传参是写代码过程中必不可少的过程,如果我们针对不同的类、函数去传入对应的不同参数,这样会带来很多弊端,
- 代码阅读性差
- 维护成本高
首先举一个例子,
class A(object): def __init__(self, param_a, param_b, param_c): pass class B(object): def __init__(self, param_d, param_e, param_f): pass
以上面代码为例,我们针对class A和class B要传入不同的6个参数parma a-f,这样带来的第一个问题是代码阅读性差,我们需要逐个去确认各个参数的类型。其次,就是维护成本高,我们在开发过程中需要多写很多代码,另外如果对参数进行修改的话需要找到使用这个参数的地方进行修改,所以,非常繁琐。
在项目开发中,针对传参问题常用的解决方法就是写一个配置文件,把项目中需要的参数列在这个文件中,然后写一个函数去解析配置文件,这样如果我们想查看不同变量的类型或者修改参数,只需要去查看这个文件即可。
举例,下面是一个配置文件,格式可以自定,
# 配置文件 param_a: 1 param_b: 2 param_c: 3 param_d: 4 param_e: 5 param_f: 6
然后写一下修改后的Python程序,
class Config(object): def __init__(self, path): pass conf = Config(config_path) class A(object): def __init__(self, conf): pass class B(object): def __init__(self, conf): pass
这样的话代码更加简洁,而且维护效率更高。
我们可以自己写一个解析配置文件的函数,可以定制化去写一些解析方法,当然,也可以使用本文的主角-ConfigParser,它是Python自带模块,该模块提供了ConfigParser类,该类实现了一种基本配置语言,它提供的结构类似于Microsoft Windows INI文件中的结构,你可以使用它来编写可由最终用户轻松定制的Python程序,下面就来看一下ConfigParser的使用方法。
ConfigParser
首先来看一下一个基本的配置文件,我们可以把它命名为config.ini,
[DEFAULT] ServerAliveInterval = 45 Compression = yes CompressionLevel = 9 ForwardX11 = yes [bitbucket.org] User = hg [topsecret.server.com] Port = 50022 ForwardX11 = no
来看一下上述配置文件的内容,本质上ini配置文件是由多个section组成的,上述示例中DEFAULT
、bitbucket.org
、topsecret.server.com
就是所说的section,每个section包含若干个键值对,这里的键值是不区分大小写的,写入时会把键值存为小写。其中,DEFAULT比较特殊一些,它为其他section中键值提供默认值。然后,通过ConfigParser解析配置文件之后,我们就可以像读取Python中字典的方式读取每个section下的配置参数。另外,需要强调的是,ConfigParser不仅可以读取配置文件,还可以写入配置文件,下面就分别从配置文件的写入和读取进行讲解。写入就如同前面所说,可以像写入字典那样去写入一个配置文件,
import configparser config = configparser.ConfigParser() # 用法1 config["DEFAULT"] = { "Country": "China", "Max_Iter": "2", } # 用法2 config["section_1"] = {} config["section_1"]["a"] = "2" # 用法3 config["section_2"] = {} section2 = config["section_2"] section2["b"] = "2" config['DEFAULT']['ForwardX11'] = 'yes' with open("example.ini", "w") as fp: config.write(fp)
上面示例中给出3种写入的用法,
- 用法1:直接把section的键值对写入到字典
- 用法2:逐级的添加键值对
- 用法3:把初始化的section先赋值给一个变量,然后添加其他键值对
然后执行程序,就可以把配置信息写入到example.ini
文件,
[DEFAULT] country = China max_iter = 2 forwardx11 = yes [section_1] a = 2 [section_2] b = 2
读取前面介绍了写入,接下来可以介绍读取config配置文件,这也是在开发过程中主要的应用场景。先查看一下有哪些section,
import configparser config = configparser.ConfigParser() sections = config.sections() print(sections) # 输出 []
输出为空,这是因为我们没有读取配置文件,下面我们读取配置文件之后再看一下,
config.read("example.ini") sections = config.sections() print(sections) # 输出 ['section_1', 'section_2']
下面,我们就可以像读取字典一样去获取我们想要的配置参数,
print(config["DEFAULT"]["country"]) print(config["section_1"]["a"]) # 输出 China 2
数据类型上述更多的是体现了ConfigParser的便捷性,但是还没有体现出去的在开发过程中的真正优势,它支持按照数据类型读取配置参数。ConfigParser在解析配置文件时,它是不区分配置参数类型的,它读取到内存都是将其视为字符串,如果按照其他数据类型读取的话,它会自行将它转化成对应的数据类型。ConfigParser支持读取整型、浮点型、布尔型的值,由于其中布尔型的值比较特别,下面首先介绍一下布尔型参数的读取。布尔型我们可以通过'yes'/'no','on'/'off','true'/'false'和'1'/'0'等来设定布尔型的参数,然后通过getboolean函数读取对应的参数。例如,下面这个配置文件,
[DEFAULT]
country = China
max_iter = 2
a = true
b = off
c = 1
读取并输出结果,
print(config["DEFAULT"].getboolean("a")) print(config["DEFAULT"].getboolean("b")) print(config["DEFAULT"].getboolean("c")) # 输出 True False True
整型和浮点型可以通过getint
和getfloat
来读取整型和浮点型的参数,例如,配置文件如下,
[section_1] a = 2 b = 1.2
读取并输出结果,
print(config["section_1"].getint("a")) print(config["section_1"].getfloat("b")) # 输出 2 1.2
这里需要注意一下,用getfloat去读取整型参数会把它转化为浮点型,但是不能用getint去读取浮点型数据,这样会报错。备选值我们可以像用字典那样,使用get方法获取备选值。所谓备选值,就是当我们从一个section中读取某个值时,可以给它一个备用的值,例如,
test_dict = {} test_dict.get("a", 2)
test_dict中没有键值a,当我们读取时无法读取到它的值,这时候就会给它一些备选值2。ConfigParser也支持备选值,但是需要注意的是,在ConfigParser中DEFAULT的优先级要高于备选值,如果DEFAULT中有对应的值,即便我们设置了备选值也不会起作用。当前配置文件如下,
[DEFAULT] country = China max_iter = 2 a = true b = off c = 1 [section_1] a = 2 b = 1.2 [section_2] b = 2
然后设置备选值,
print(top.get("max_iter", fallback=4)) print(top.get("e", fallback=5)) # 输出 2 5
从第一个结果可以看出,由于max_iter在DEFAULT中有对应的键值,几遍设置了备选值也不会起作用,但是如果DEFAULT中没有对应的值,它会读取fallback
中的值作为输出。插值ConfigParser不仅支持以上功能,还支持插值,这样的话它可以调用其他section或者可选参数vars中的变量。调用其他section的参数可以通过类似shell命令的变量调用的方式进行使用,下面来看一下示例,
[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}
可以通过${section:option}
的方式调用其他section的参数,需要注意的是,使用插值时需要引入ExtendedInterpolation、或者BasicInterpolation,例如,
from configparser import ConfigParser, ExtendedInterpolation config = ConfigParser(interpolation=ExtendedInterpolation()) config.read("example.ini") top = config["Arthur"] print(top.get('python_dir')) # 输出 /System/Library/Frameworks//Python/Versions/3.2
另外,还可以通过设置可选参数vars来使用插值,其中vars是一个字典类型的参数,如果vars中有对应的字符串,它会首先从vars中读取,
print(top.get('python_dir', 'foo', vars={'bar': 'Documentation', 'baz': 'evil', 'python_dir': "hello world"})) # 输出 hello world
上述就是ConfigParser的常见用法。