|
文章目录Python和TOML:新最好的朋友使用Python加载TOML使用tomli或tomllib读取TOML文档比较TOML类型和Python类型在项目中使用配置文件将Python对象转换为TOML将字典转换为TOML通过tomli_w写TOML文档创建新的TOML文件格式和样式用`tomlkit`从头开始创建TOML更新现有的TOML文件将TOML表示为tomlkit对象无损读取和写入TOML总结Python和TOML:新最好的朋友原文:《PythonandTOML:NewBestFriends》使用Python加载TOML使用tomli或tomlib加载TOML文档。使用tomli或tomllib读取TOML文档在python3.11中,TOML支持已经内置在标准库中,tomllib模块可以读取和解析TOML文档。如果你使用的是Python3.11及以后的版本,可以直接使用tomllib模块替代tomli我们先创建一个TOML文件tic_tac_toe.toml:#tic_tac_toe.toml[user]player_x.color="blue"player_o.color="green"[constant]board_size=3[server]url="https://tictactoe.example.com"1234567891011用pip安装tomli:python-mpipinstalltomli1tomli模块仅公开两个函数:load():从文件对象加载TOMLloads():从字符串加载TOML文档。首先用load()函数加载TOML文件:importtomliwithopen("tic_tac_toe.toml",mode="rb")asfp:config=tomli.load(fp)123然后可以查看config的内容:>>>config{'user':{'player_x':{'color':'blue'},'player_o':{'color':'green'}},'constant':{'board_size':3},'server':{'url':'https://tictactoe.example.com'}}>>>config["user"]["player_o"]{'color':'green'}>>>config["server"]["url"]'https://tictactoe.example.com'12345678910TOML文档在Python中表示为字典。TOML文件中的所有表和子表都显示为嵌套字典如果已经将TOML文档表示为字符串,则可以使用loads()代替load()。>>>importtomli>>>toml_str="""...offset_date-time_utc=2021-01-1200:23:45Z...potpourri=["flower",1749,{symbol="X",color="blue"},1994-02-14]...""">>>tomli.loads(toml_str){'offset_date-time_utc':datetime.datetime(2021,1,12,0,23,45,tzinfo=datetime.timezone.utc),'potpourri':['flower',1749,{'symbol':'X','color':'blue'},datetime.date(1994,2,14)]}12345678910111213注意,TOML时间和日期类型由Pythondatetime的类型表示load()和loads()之间的一个区别是,当您使用loads()时,您使用的是常规字符串而不是字节。在这种情况下,tomli假设您已正确处理编码。如果你希望自动设置tomli和tomllib。如果Python版本是3.11及以后的版本,则直接使用tomllib模块,否则安装并使用tomli模块。在requirements.txt写入:tomli>=1.1.0;python_version>>importtomli>>>fromdecimalimportDecimal>>>ts=tomli.loads(..."ts=2_459_772.084027777777778",...parse_float=Decimal,...)["ts"]>>>tsDecimal('2459772.084027777777778')>>>seconds=(ts-int(ts))*86_400>>>secondsDecimal('7260.000000000019200')>>>seconds-7260Decimal('1.9200E-11')123456789101112131415在项目中使用配置文件我们希望配置文件只读取一次,然后各个地方的代码都可以使用这个配置。实际上,Python的import机制可以帮助我们实现这一点。import模块时,会缓存模块的内容,所以只有第一次import时会读取配置文件。下面看一个具体的例子。我们的配置文件为tic_tac_toe.toml:#tic_tac_toe.toml[user]player_x.color="blue"player_o.color="green"[constant]board_size=3[server]url="https://tictactoe.example.com"1234567891011创建config/目录:config/├──__init__.py└──tic_tac_toe.toml123名为__init__.py的文件在Python中扮演着特殊的角色。它们将包含目录标记为包。此外,__init__.py定义的名称通过包公开。下面我们在__init__.py读取配置文件:#__init__.pyimportpathlibimporttomlipath=pathlib.Path(__file__).parent/"tic_tac_toe.toml"withpath.open(mode="rb")asfp:tic_tac_toe=tomli.load(fp)12345678在config目录下启动解释器:>>>importconfig>>>config.pathPosixPath('/home/realpython/config/tic_tac_toe.toml')>>>config.tic_tac_toe{'user':{'player_x':{'color':'blue'},'player_o':{'color':'green'}},'constant':{'board_size':3},'server':{'url':'https://tictactoe.example.com'}}12345678现在,您可以通过将config/目录复制到项目中并将井字游戏配置替换为您自己的设置,将配置集成到现有项目中。在代码文件中,您可能希望为配置导入添加别名,以便更方便地访问您的设置:>>>fromconfigimporttic_tac_toeasCFG>>>CFG["user"]["player_x"]["color"]'blue'1234将Python对象转换为TOMLTOML文档通常是手写的,因为它们主要用作配置。不过,有时您可能需要将嵌套字典转换为TOML文档。将字典转换为TOML{"user":{"player_x":{"symbol":"X","color":"blue","ai":True},"player_o":{"symbol":"O","color":"green","ai":False},"ai_skill":0.85,},"board_size":3,"server":{"url":"https://tictactoe.example.com"},}123456789'运行运行在本小节中,您将编写一个简化的TOML编写器,该编写器能够将此字典编写为TOML文档。创建一个新文件to_toml.py。首先,编写一个名为_dumps_value()的辅助函数。此函数将获取某个值,并根据值类型返回其TOML表示形式。#to_toml.pydef_dumps_value(value):ifisinstance(value,bool):return"true"ifvalueelse"false"elifisinstance(value,(int,float)):returnstr(value)elifisinstance(value,str):returnf'"{value}"'elifisinstance(value,list):returnf"[{','.join(_dumps_value(v)forvinvalue)}]"else:raiseTypeError(f"{type(value).__name__}{value!r}isnotsupported")12345678910111213'运行运行接下来,您将添加处理表的代码。main函数遍历字典,并将每个项目转换为键值对。如果该值恰好是字典,则您将添加一个表头并递归填写该表:#to_toml.py#...defdumps(toml_dict,table=""):toml=[]forkey,valueintoml_dict.items():ifisinstance(value,dict):table_key=f"{table}.{key}"iftableelsekeytoml.append(f"\n[{table_key}]\n{dumps(value,table_key)}")else:toml.append(f"{key}={_dumps_value(value)}")return"\n".join(toml)12345678910111213'运行运行这在处理嵌套时会出现顺序问题,我们进行排序来修复这个问题。#to_toml.py#...defdumps(toml_dict,table=""):deftables_at_end(item):_,value=itemreturnisinstance(value,dict)toml=[]forkey,valueinsorted(toml_dict.items(),key=tables_at_end):ifisinstance(value,dict):table_key=f"{table}.{key}"iftableelsekeytoml.append(f"\n[{table_key}]\n{dumps(value,table_key)}")else:toml.append(f"{key}={_dumps_value(value)}")return"\n".join(toml)1234567891011121314151617'运行运行通过tomli_w写TOML文档tomli_w有两个函数:dump()和dumps()。类似于tomli的load()和loads()。dump()写入文件,dumps()写入字符串。Python3.11中的新tomllib库不包括dump()和dumps()需要安装tomli_w:python-mpipinstalltomli_w1现在我们可以容易地将字典导出为TOML文档。>>>importtomli_w>>>config={..."user":{..."player_x":{"symbol":"X","color":"blue","ai":True},..."player_o":{"symbol":"O","color":"green","ai":False},..."ai_skill":0.85,...},..."board_size":3,..."server":{"url":"https://tictactoe.example.com"},...}>>>print(tomli_w.dumps(config))board_size=3[user]ai_skill=0.85[user.player_x]symbol="X"color="blue"ai=true[user.player_o]symbol="O"color="green"ai=false[server]url="https://tictactoe.example.com"1234567891011121314151617181920212223242526272829可以使用dump()将配置写入文件:>>>withopen("tic-tac-toe-config.toml",mode="wb")asfp:...tomli_w.dump(config,fp)...123创建新的TOML文件在本节中,您将首先探索如何设置TOML文档的格式,以使其更易于用户使用。然后,您将尝试另一个名为tomlkit的库,您可以使用它来完全控制TOML文档。格式和样式通常,TOML文件中会忽略空格。您可以利用这一点使配置文件井井有条、可读且直观。此外,#符号将该行的其余部分标记为注释。从某种意义上说,TOML文档没有样式指南,因为PEP8是Python代码的样式指南。但是,该规范确实包含一些建议,同时保留了一些样式方面供您选择。为了保持一致性,您可以在项目中使用像Taplo这样的格式化程序,并将其配置文件包含在版本控制中。您也可以将其集成到编辑器中。用tomlkit从头开始创建TOMLTOMLKit最初是为Poetry项目构建的。作为依赖项管理的一部分,Poetry会操作pyproject.toml该文件。但是,由于此文件用于多种用途,因此Poetry必须保留文件中的样式和注释。首先通过pip安装tomlkit:python-mpipinstalltomlkit然后,我们确认tomlkit可以保留样式和注释:>>>importtomlkit>>>toml_data="""...[nested]#Notnecessary......[nested.table]...string="Hello,TOML!"...weird_string='''Literal...Multiline'''...""">>>print(tomlkit.dumps(tomlkit.loads(toml_data)))[nested]#Notnecessary[nested.table]string="Hello,TOML!"weird_string='''LiteralMultiline'''>>>tomlkit.dumps(tomlkit.loads(toml_data))==toml_dataTrue1234567891011121314151617181920下面我们使用tomlkit创建一个新的TOML文件:>>>fromtomlkitimportcomment,document,nl,table>>>toml=document()>>>toml.add(comment("WrittenbyTOMLKit"))>>>toml.add(nl())>>>toml.add("board_size",3)123456调用document()创建一个TOML文档实例。然后,您可以使用.add()向此文档添加不同的对象,例如注释、换行符、键值对和表。您可以使用dump()或dumps()如上所述toml转换为实际的TOML文档,也可以使用.as_string()方法:>>>print(toml.as_string())#WrittenbyTOMLKitboard_size=31234通过添加几个表来继续您的示例:>>>player_x=table()>>>player_x.add("symbol","X")>>>player_x.add("color","blue")>>>player_x.comment("Startplayer")>>>toml.add("player_x",player_x)>>>player_o=table()>>>player_o.update({"symbol":"O","color":"green"})>>>toml["player_o"]=player_o123456789您可以通过调用table()创建表并向其添加内容。可以通过.add()添加键和值,也可以使用.update()直接从字典添加键和值。更新现有的TOML文件将TOML表示为tomlkit对象下面仔细研究一下如何用ttomlki表示TOML文档。首先,创建tic-tac-toe-config.toml:#tic-tac-toe-config.tomlboard_size=3[user]ai_skill=0.85#Anumberbetween0(random)and1(expert)[user.player_x]symbol="X"color="blue"ai=true[user.player_o]symbol="O"color="green"ai=false#Settingsusedwhendeployingtheapplication[server]url="https://tictactoe.example.com"1234567891011121314151617181920打开REPL会话并使用以下命令tomlkit加载此文档:>>>importtomlkit>>>withopen("tic-tac-toe-config.toml",mode="rt",encoding="utf-8")asfp:...config=tomlkit.load(fp)...>>>config{'board_size':3,'user':{'ai_skill':0.85,'player_x':{...}}}>>>type(config)123456789load()用于从文件加载TOML文档。config看起来像一个字典。然而,深入挖掘,你会发现它是TOMLDocument类型。我们可以用[]访问:>>>config["user"]["player_o"]["color"]'green'>>>type(config["user"]["player_o"]["color"])>>>config["user"]["player_o"]["color"].upper()'GREEN'12345678发现其中的值也是特殊的tomlkit数据类型。不过我们可以像普通的Python对象一样使用它们。例如,可以使用.upper()字符串方法。特殊数据类型的一个优点是,它们允许您访问有关文档的元信息,包括注释和缩进:>>>config["user"]["ai_skill"]0.85>>>config["user"]["ai_skill"].trivia.comment'#Anumberbetween0(random)and1(expert)'>>>config["user"]["player_x"].trivia.indent''12345678如上所述,您通常可以将这些特殊对象视为原生Python对象。事实上,它们继承自相应的Python对象。但是,如果你真的需要,你可以用.unwrap()它们转换为普通的Python:>>>config["board_size"]**29>>>isinstance(config["board_size"],int)True>>>config["board_size"].unwrap()3>>>type(config["board_size"].unwrap())1234567891011无损读取和写入TOML在本小节中,您将加载现有的TOML文件,并在将其写回磁盘之前对其进行一些更改。首先加载您在上一小节中使用的相同TOML文件:>>>importtomlkit>>>withopen("tic-tac-toe-config.toml",mode="rt",encoding="utf-8")asfp:...config=tomlkit.load(fp)...1234现在,你可以用.add()添加新元素,但无法用.add()更新现有key的值。>>>config.add("app_name","Tic-Tac-Toe"){'board_size':3,'app_name':'Tic-Tac-Toe','user':{...}}>>>config["user"].add("ai_skill",0.6)Traceback(mostrecentcalllast):...KeyAlreadyPresent:Key"ai_skill"alreadyexists.1234567不过,我们可以像字典一样:>>>config["user"]["ai_skill"]=0.6>>>print(config["user"].as_string())ai_skill=0.6#Anumberbetween0(random)and1(expert)[user.player_x]symbol="X"color="blue"ai=true[user.player_o]symbol="O"color="green"ai=false12345678910111213当您更新这样的值时,tomlkit仍然会保留样式和注释。如您所见,关于ai_skill的注释保持不变。部分tomlkit支持所谓的Fluent接口(链式调用)。在实践中,这意味着返回更新的对象>>>fromtomlkitimportaot,comment,inline_table,nl,table>>>player_data=[...{"user":"gah","first_name":"GeirArne","last_name":"Hjelle"},...{"user":"tompw","first_name":"Tom","last_name":"Preston-Werner"},...]>>>players=aot()>>>forplayerinplayer_data:...players.append(...table()....add("username",player["user"])....add("name",...inline_table()....add("first",player["first_name"])....add("last",player["last_name"])...)...)...>>>config.add(nl()).add(comment("Players")).add("players",players)12345678910111213141516171819完成对配置的更新后,现在可以将其写回同一文件:>>>withopen("tic-tac-toe-config.toml",mode="wt",encoding="utf-8")asfp:...tomlkit.dump(config,fp)12打开tic-tac-toe-config.toml并注意您的更新已包含在内。同时,保留了原有的风格。总结现在你已经了解TOML语法以及在Python中使用它的方式。当你需要一个配置文件时,推荐考虑使用TOML。
|
|