首頁 >後端開發 >Python教學 >超全! Python 中常見的設定檔寫法

超全! Python 中常見的設定檔寫法

PHPz
PHPz轉載
2023-04-13 08:31:051359瀏覽

超全! Python 中常見的設定檔寫法

為什麼要寫設定檔

在開發過程中,我們常常會用到一些固定參數或是常數。對於這些較為固定且常用到的部分,往往會將其寫到一個固定檔案中,避免在不同的模組程式碼中重複出現從而保持核心程式碼整潔。

這個固定文件我們可以直接寫成一個.py 文件,例如settings.py 或config.py,這樣的好處就是能夠在同一工程下直接透過import 來導入當中的部分;但如果我們需要在其他非Python 的平台進行設定檔共用時,寫成單一.py 就不是一個很好的選擇。

這時我們就應該選擇通用的設定檔類型來作為儲存這些固定的部分。目前常用且流行的設定檔格式類型主要有 ini、json、toml、yaml、xml 等,這些類型的設定檔我們都可以透過標準函式庫或第三方函式庫來進行解析。

ini

ini 即 Initialize 初始化之意,早期是在 Windows 上設定檔的儲存格式。 ini 檔案的寫法簡單易懂,往往比較簡單,通常由節(Section)、鍵(key)和值(value)組成,就像以下形式:

[localdb]
host = 127.0.0.1
user = root
password = 123456
port = 3306
database = mysql

Python 本身內建的configparser 標準庫,我們直接就可以用來對ini 檔進行解析。如我們將上述內容保存在一個名為 db.ini 的檔案中,然後使用 read() 方法來進行解析和讀取,最後透過 items() 方法來取得指定節點下的所有鍵值對。

>>> from configparser import ConfigParser
>>> cfg = ConfigParser()
>>> cfg.read("/Users/Bobot/db.ini")
['/Users/Bobot/db.ini']
>>> cfg.items("localdb")
[('host', '127.0.0.1'), ('user', 'root'), ('password', '123456'), ('port', '3306'), ('database', 'mysql')]

要注意的是,configparser 預設將值以字串的形式呈現,所以這就是為什麼我們在 db.ini 檔案中沒有加引號而是直接將字面量寫在上面的原因。

取得到鍵值對後,我其實直接就將其轉換成字典,然後透過解包的方式進行穿參,保持程式碼簡潔:

#!pip install pymysql
import pymysql
from configparser import ConfigParser
cfg = ConfigParser()
cfg.read("/Users/Bobot/db.ini")
db_cfg = dict(cfg.items("localdb"))
con = pymysql.connect(**db_cfg)

json

json 格式可以說是我們常見的一種文件形式了,也是目前在網路上較為流行的一種資料交換格式。除此之外,json 有時也是設定檔的一種。

例如 npm(JavaScript 套件管理工具類似 Python 的 pip)、以及微軟出品的目前被廣泛使用的 VSCode 編輯器,都使用 json 編寫配置參數。

和 configparser 一樣,Python 也內建了 json 標準函式庫,可以透過 load() 和 loads() 方法來匯入檔案式和字串的 json 內容。

{
 "localdb":{
 "host": "127.0.0.1",
 "user": "root",
 "password": "123456",
 "port": 3306,
 "database": "mysql"
 }
}

我們將上述內容儲存為 db.json 後進行讀取和解析,json 函式庫讀取 json 檔案相對簡單容易,而且很容易解析成 Python 的字典物件。

>>> import json
>>> from pprint import pprint
>>>
>>> with open('/Users/Bobot/db.json') as j:
... cfg = json.load(j)['localdb']
...
>>> pprint(cfg)
{'database': 'mysql',
'host': '127.0.0.1',
'password': '123456',
'port': 3306,
'user': 'root'}

使用json 檔案配置的缺點就是語法標準嚴格限制,為人所詬病之一的就是無法在當中寫註釋,除非採取json 類型的其他超集作為替代方案(VSCode 中能寫註釋的json 參數設定檔便是代替方案的一種);同時存在嵌套過深的問題,容易導致出錯,不宜用來寫過長或複雜的參數配置資訊。

toml

toml 格式(或 tml 格式)是 Github 共同創辦人 Tom Preston-Werner 所提出的一種設定檔格式。根據維基百科的資料,toml 最開始提出時是在2013年7月份,距今已有七年時間;它在某些方面也與後面要談到的yaml 文件有些類似,但如果當你知道yaml 的規範有幾十頁(沒有錯,真的就是幾十頁…)的時候,可能你真的不太願意去寫那麼複雜的配置文件,toml 格式則倒是個不錯的選擇。

toml 格式大致如下:

超全! Python 中常見的設定檔寫法

01-toml樣式

#從這裡可以看出toml 有點類似前面所講的ini文件。但是它比 ini 擴展了更多的內容。

在範例圖片中我們可以看到,除了基本的字串以外,例如時間戳、布林值、陣列等都進一步支持,而且樣式和 Python 的原生寫法十分類似。

當然這裡不會過多介紹 toml 格式的一些規範說明,有人已經對官方的規範文檔進行了翻譯,有興趣的朋友可以直接查閱。

這麼契合Python 方式的配置文件類型已經有開發者造出了相應的“輪子”,目前在Github 上Stars 數最多的是則是uiri/toml 的版本,不過該版本僅通過了v0.5 版本toml 規範,但在使用上還蠻簡潔的,我們可以透過pip 指令進行安裝

pip install toml

該函式庫的解析方式很簡單,也有點類似json 函式庫的解析用法,即透過load() 或loads() 來進行解析;同理轉換並導出也是同樣類似的用法。

例如我們現在將以下內容寫入到 config.toml 中:

[mysql]
host = "127.0.0.1"
user = "root"
port = 3306
database = "test"
 [mysql.parameters]
 pool_size = 5
 charset = "utf8"
 [mysql.fields]
 pandas_cols = [ "id", "name", "age", "date"]

紧接着我们就可以通过 toml 库中的 load() 方法来进行读取:

>>> import toml
>>> import os
>>> from pprint import pprint
>>> cfg = toml.load(os.path.expanduser("~/Desktop/config.toml"))
>>> pprint(cfg)
{'mysql': {'database': 'test',
'fields': {'pandas_cols': ['id', 'name', 'age', 'date']},
'host': '127.0.0.1',
'parameters': {'charset': 'utf8', 'pool_size': 5},
'port': 3306,
'user': 'root'}}

可以看到 toml 文件被间接地转化成了字典类型,当然这也就是 json 版的写法(将单引号替换成双引号即可),方便我们后续调用或者传参。

yaml

yaml 格式(或 yml 格式)是目前较为流行的一种配置文件,它早在 2001 由一个名为 Clark Evans 的人提出;同时它也是目前被广泛使用的配置文件类型,典型的就是 Docker 容器里的 docker-compose.yml 配置文件,如果经常使用 Docker 进行部署的人对此不会陌生。

yaml 文件的设计从 Python、XML 等地方获取灵感,所以在使用时能很清楚地看到这些部分的影子。

在上一节 toml 内容里我曾提到,yaml 的规范内容可以说是冗长和复杂,足足有80页之多(斗尊强者,恐怖如斯……)。

超全! Python 中常見的設定檔寫法

02-yaml规范页数

所以感兴趣的朋友可以再自行了解相关用法。

YAML 官方早已经提供了相应的 Python 库进行支持,即 PyYAML;当然也同样需要我们事先进行安装:

pip install pyyaml

同 json 库和 toml 库一样,通过 load() 方法来进行加载。

需要注意的是,使用 load() 方法会存在一定的安全隐患,从思科 Talos 的这份报告中我们可以看到,如果加载了未知或不信任的 yaml 文件,那么有可能会存在被攻击的风险和网络安全隐患,因为它能够直接调用相应的 Python 函数来执行为攻击者所需要的命令,比如说在 yaml 文件中写入这么一段:

# 使用Linux和macOS的朋友不要轻易尝试
!!python/object/apply:os.system ["rm -rf /"]

因此最好是使用 safe_load() 来代替 load() 方法。

这和 Python 内置的 string 标准库中 Template 类的 substitute() 模板方法一样存在着同样的安全隐患,所以使用 safe_substitute() 来替代是一样的道理。

如我们现在将之前的一些配置信息写入 config.yaml 文件中:

mysql:
 host: "127.0.0.1"
 port: 3306
 user: "root"
 password: "123456"
 database: "test"
 parameter:
 pool_size: 5
 charset: "utf8"
 fields:
pandas_cols:
 - id
 - name
 - age
 - date

然后我们通过 safe_load() 方法进行解析:

>>> import os
>>> from pprint import pprint
>>>
>>> with open(os.path.expanduser("~/config.yaml"), "r") as config:
... cfg = yaml.safe_load(config)
...
>>> pprint(cfg)
{'mysql': {'database': 'test',
'fields': {'pandas_cols': ['id', 'name', 'age', 'date']},
'host': '127.0.0.1',
'parameter': {'charset': 'utf8', 'pool_size': 5},
'password': '123456',
'port': 3306,
'user': 'root'}}

可以看到最后结果和前面的 toml 库的解析结果基本一致。

结尾

本文列举了一些主流且常见的配置文件类型及其 Python 的读取方法,可能有的读者会发现当中没有 xml 格式类型的内容。对于 xml 配置文件可能与 Java 系语言打交道的朋友遇见得会多一些,但 xml 文件的可读性实在是让人望而生畏;对 xml 文件不了解的朋友可以使用 Chrome 浏览器随便进入一个网站然后按下 F12 进入开发者后查看那密密麻麻的 html 元素便是 .xml 的缩影。

除了这些主流的配置文件类型之外,像一些 .cfg、.properties 等都可以作为配置文件,甚至和开头提到的那样,你单独用一个 .py 文件来书写各类配置信息作为配置文件进行导入都是没问题,只是在跨语言共享时可能会有些障碍。因此本文就不过多介绍,感兴趣的朋友可以进一步自行了解。

在本文里列举的配置文件类型其复杂性由上到下依次增加:ini

以上是超全! Python 中常見的設定檔寫法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:51cto.com。如有侵權,請聯絡admin@php.cn刪除