我正在使用YAML配置文件。这是在Python中加载我的配置的代码:
import os
import yaml
with open('./config.yml') as file:
config = yaml.safe_load(file)
这段代码实际上创建了一个字典。现在的问题是,为了访问值,我需要使用大量的括号。
YAML:
mysql:
user:
pass: secret
蟒蛇:
import os
import yaml
with open('./config.yml') as file:
config = yaml.safe_load(file)
print(config['mysql']['user']['pass']) # <--
我更喜欢这样的东西(点符号):
config('mysql.user.pass')
因此,我的想法是利用PyStache render()接口。
import os
import yaml
with open('./config.yml') as file:
config = yaml.safe_load(file)
import pystache
def get_config_value( yml_path, config ):
return pystache.render('{{' + yml_path + '}}', config)
get_config_value('mysql.user.pass', config)
那将是一个“好的”解决方案吗?如果没有,什么是更好的选择?
附加问题[已解决]
我决定使用IljaEverilä的解决方案。但是现在我还有一个问题:如何围绕DotConf创建包装器Config类?
以下代码不起作用,但希望您能理解我要执行的操作:
class Config( DotDict ):
def __init__( self ):
with open('./config.yml') as file:
DotDict.__init__(yaml.safe_load(file))
config = Config()
print(config.django.admin.user)
错误:
AttributeError: 'super' object has no attribute '__getattr__'
解
您只需要传递self
给超类的构造函数即可。
DotDict.__init__(self, yaml.safe_load(file))
更好的解决方法(IljaEverilä)
super().__init__(yaml.safe_load(file))
您可以用来reduce
从配置中提取值:
In [41]: config = {'asdf': {'asdf': {'qwer': 1}}}
In [42]: from functools import reduce
...:
...: def get_config_value(key, cfg):
...: return reduce(lambda c, k: c[k], key.split('.'), cfg)
...:
In [43]: get_config_value('asdf.asdf.qwer', config)
Out[43]: 1
如果您的YAML使用的语言子集非常有限,则此解决方案易于维护并且几乎没有新的边缘情况。
使用适当的YAML解析器和工具,例如此答案:
一方面,您的示例通过使用正确的方法,get_config_value('mysql.user.pass', config)
而不是通过属性来解决点分访问。我不确定您是否意识到您是故意不做更直观的事情:
print(config.mysql.user.pass)
即使是重载__getattr__
,passPython语言元素也无法正常工作。
但是,您的示例仅描述了非常有限的YAML文件子集,因为它不涉及任何序列集合,也不涉及任何复杂的键。
如果您想覆盖的范围不只微小的子集,可以例如扩展以下功能强大的往返对象ruamel.yaml:¹
import ruamel.yaml
def mapping_string_access(self, s, delimiter=None, key_delim=None):
def p(v):
try:
v = int(v)
except:
pass
return v
# possible extend for primitives like float, datetime, booleans, etc.
if delimiter is None:
delimiter = '.'
if key_delim is None:
key_delim = ','
try:
key, rest = s.split(delimiter, 1)
except ValueError:
key, rest = s, None
if key_delim in key:
key = tuple((p(key) for key in key.split(key_delim)))
else:
key = p(key)
if rest is None:
return self[key]
return self[key].string_access(rest, delimiter, key_delim)
ruamel.yaml.comments.CommentedMap.string_access = mapping_string_access
def sequence_string_access(self, s, delimiter=None, key_delim=None):
if delimiter is None:
delimiter = '.'
try:
key, rest = s.split(delimiter, 1)
except ValueError:
key, rest = s, None
key = int(key)
if rest is None:
return self[key]
return self[key].string_access(rest, delimiter, key_delim)
ruamel.yaml.comments.CommentedSeq.string_access = sequence_string_access
设置完成后,您可以运行以下命令:
yaml_str = """\
mysql:
user:
pass: secret
list: [a: 1, b: 2, c: 3]
[2016, 9, 14]: some date
42: some answer
"""
yaml = ruamel.yaml.YAML()
config = yaml.load(yaml_str)
def get_config_value(path, data, **kw):
return data.string_access(path, **kw)
print(get_config_value('mysql.user.pass', config))
print(get_config_value('mysql:user:pass', config, delimiter=":"))
print(get_config_value('mysql.list.1.b', config))
print(get_config_value('mysql.2016,9,14', config))
print(config.string_access('mysql.42'))
给予:
secret
secret
2
some date
some answer
这表明您只需花更多的精力和很少的额外工作,就可以灵活地对许多YAML文件进行点扩展访问,而不仅仅是那些由以字符串标量为键的递归映射组成的文件。
config.string_access(mysql.user.pass)
而不是定义和使用get_config_value()
简要说明一下(不要太在意),您可以创建一个包装器,以允许使用属性访问:
In [47]: class DotConfig:
...:
...: def __init__(self, cfg):
...: self._cfg = cfg
...: def __getattr__(self, k):
...: v = self._cfg[k]
...: if isinstance(v, dict):
...: return DotConfig(v)
...: return v
...:
In [48]: DotConfig(config).asdf.asdf.qwer
Out[48]: 1
请注意,这对于诸如“ as”,“ pass”,“ if”之类的关键字失败。
最后,您可能会变得非常疯狂(阅读:可能不是一个好主意),并进行自定义dict
以处理点缀字符串和元组键(作为特殊情况),并可以对混合中抛出的项进行属性访问(有其局限性):
In [58]: class DotDict(dict):
...:
...: # update, __setitem__ etc. omitted, but required if
...: # one tries to set items using dot notation. Essentially
...: # this is a read-only view.
...:
...: def __getattr__(self, k):
...: try:
...: v = self[k]
...: except KeyError:
...: return super().__getattr__(k)
...: if isinstance(v, dict):
...: return DotDict(v)
...: return v
...:
...: def __getitem__(self, k):
...: if isinstance(k, str) and '.' in k:
...: k = k.split('.')
...: if isinstance(k, (list, tuple)):
...: return reduce(lambda d, kk: d[kk], k, self)
...: return super().__getitem__(k)
...:
...: def get(self, k, default=None):
...: if isinstance(k, str) and '.' in k:
...: try:
...: return self[k]
...: except KeyError:
...: return default
...: return super().get(k, default=default)
...:
In [59]: dotconf = DotDict(config)
In [60]: dotconf['asdf.asdf.qwer']
Out[60]: 1
In [61]: dotconf['asdf', 'asdf', 'qwer']
Out[61]: 1
In [62]: dotconf.asdf.asdf.qwer
Out[62]: 1
In [63]: dotconf.get('asdf.asdf.qwer')
Out[63]: 1
In [64]: dotconf.get('asdf.asdf.asdf')
In [65]: dotconf.get('asdf.asdf.asdf', 'Nope')
Out[65]: 'Nope'
问题内容: 除了显而易见的事实,第一种形式可以使用变量而不仅仅是字符串文字,是否有理由在另一种形式上使用另一种形式?如果是这样,在哪种情况下? 在代码中: 上下文:我已经编写了一个代码生成器来生成这些表达式,我想知道哪种更好。 问题答案: 方括号符号允许使用点符号不能使用的字符: 包括非ASCII(UTF-8)字符,如。 其次,方括号表示法在处理以可预测的方式变化的属性名称时非常有用: Round
问题内容: 如何通过点“。”访问Python词典成员? 例如,我想写而不是写。 我也想以这种方式访问嵌套的字典。例如 将指 问题答案: 你可以使用我刚刚制作的此类来做。通过此类,你可以像其他字典(包括json序列化)一样使用该对象,也可以使用点符号。希望对你有所帮助: 用法示例:
我花了整整一周的时间与Gmail API做斗争,但没有成功。 我的任务: 使用自定义域创建邮箱 使用服务帐户通过Gmail API连接到此邮箱 我所做的: 然而,我总是会遇到一个错误: 我在设置中错过了什么?
通常在Spring EL中,您可以使用方括号中的键通过其键访问地图条目。如果spring集成消息的有效负载是,然后 提供以字符串“my key”为键的条目的值。 当使用标准Spring EL设置时,例如在单元测试中,这是使用地图的唯一方法。然而,在Spring integration flows使用的表达式中,我可以使用点符号进行映射访问。这是有效的: 我希望能够在单元测试和路由定义中使用相同的S
问题内容: 我对Cygwin很陌生。我创建了一个符号链接,如下所示 当我通过Windows 检查驱动器时,我看到一个名为的系统文件。有没有办法使Windows充当文件夹而不是系统文件? 问题答案: 从来没听说过。Cygwin不会将操作系统更新为具有符号链接,而是允许您从Cygwin Shell中“伪造”符号链接。您可以将外壳设置为使用Windows LNK文件,该文件可以执行您想要的操作,但是…
问题内容: 我是YAML的新手,一直在寻找解析YAML文件和使用/访问已解析YAML数据的方法。 我遇到过有关如何解析YAML文件的解释,例如PyYAML教程“如何在Python中解析YAML文件”,“将Python字典转换为对象? ”,但是我没有找到一个关于如何从已解析的YAML文件访问数据的简单示例。 假设我有一个YAML文件,例如: 如何访问文本“ branch1文本”? “ YAML解析和