1. 程式人生 > >python 配置檔案讀寫

python 配置檔案讀寫

轉自: orientlu 的《python 配置檔案讀寫》

原址:https://www.jianshu.com/p/aadd86f8d38e

 

 

前言

將程式碼中的配置項抽取到配置檔案中,修改配置時不需要涉及到程式碼修改,避免面對一堆令人抓狂的 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



作者:orientlu
連結:https://www.jianshu.com/p/aadd86f8d38e
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。