Python YAML参数参考(从其他参数获取值)
Python YAML parameter reference (get value from other parameter)
我试图在我的 Python 脚本中使用 YAML 配置文件,我发现的问题之一是我无法访问其他属性,所以我需要复制很多内容.
例如
root_path: /root
script_path: root_path + /scripts
这当然不存在,但是有什么办法可以实现吗?因为有很多内容我无法复制,因为当一个改变时,我需要到处改变它...
我也考虑过创建自己的连接函数
root_path: &ROOT /root
script_path: !join [*ROOT, '/scripts']
def join(loader, node):
seq = loader.construct_sequence(node)
return ''.join([str(i) for i in seq])
yaml.add_constructor('!join', join)
但是每次都需要设置一个&VARNAME
,自动设置参数key为可引用对象就好了...
你不能用 YAML 真正做到这一点。对于配置文件,您可以改为使用 ConfigParser,这样您就可以插入值,这样您的示例将如下所示:
root_path: /root
script_path: %(root_path)s/scripts
我只是稍微调整一下 YAML 解析器 ¹:
import ruamel.yaml as yaml
from ruamel.yaml.comments import CommentedMap
yaml_str = """\
root_path: /root # use this key for expansion of values
script_path: root_path + /scripts
"""
def set_item(self, key, value):
split_val = value.split(' + ', 1)
if len(split_val) > 1:
alt_key = split_val[0].strip()
if alt_key in self.keys():
value = self.get(alt_key) + split_val[1]
self._org__setitem__(key, value)
CommentedMap._org__setitem__ = CommentedMap.__setitem__
CommentedMap.__setitem__ = set_item
data = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)
print yaml.dump(data, Dumper=yaml.RoundTripDumper)
会给你:
root_path: /root # use this key for expansion of values
script_path: /root/scripts
请注意,RoundTripLoader/Dumper 组合保留了注释和键的顺序。 data
正常工作 Python dict
.
¹ 这是使用 ruamel.yaml 完成的,我是作者。它是 PyYAML 的超集,主要针对保留注释和数据往返的其他信息。
好吧,我一直在研究解决方案,因为没有什么是我想要的,而且一切都太复杂了,我想死...
此解决方案将 %something%
字符串转换为 something
的值。
完美运行,这是一个例子
root_path: /root
script_path: "%root%/scripts"
使用此方法,script_path
将变为 /root/scripts
。
def replace_values(yaml_file):
def _get(dict, list):
return reduce(lambda d, k: d[k], list, dict)
def _replace(obj):
for k, v in obj.iteritems():
if isinstance(v, dict):
_replace(v)
if isinstance(v, str):
match = re.match(r'%(.*)%', v)
if match:
reference = match.group(1).split('.')
replace = _get(yaml_file, reference)
obj[k] = re.sub(r'%.*%', replace, v)
_replace(yaml_file)
return yaml_file
用法很简单,只需以正常方式加载 Yaml
文件,然后调用 replace。
with open(config_file.get(options.env), 'r') as ymlfile:
config = yaml.load(ymlfile)
config = replace_values(config)
然后我们的新 config
将存储替换的值,当然它不会覆盖原始 .yml
文件。我希望你觉得这很有用,我真的很喜欢,因为这正是我所需要的。
我使用了百分比 %
标记,但您可以将其更改为您想要的任何内容并更改方法以使其与正则表达式一起使用(正则表达式使用一些标记,这就是为什么我使用 %
)
我试图在我的 Python 脚本中使用 YAML 配置文件,我发现的问题之一是我无法访问其他属性,所以我需要复制很多内容.
例如
root_path: /root
script_path: root_path + /scripts
这当然不存在,但是有什么办法可以实现吗?因为有很多内容我无法复制,因为当一个改变时,我需要到处改变它...
我也考虑过创建自己的连接函数
root_path: &ROOT /root
script_path: !join [*ROOT, '/scripts']
def join(loader, node):
seq = loader.construct_sequence(node)
return ''.join([str(i) for i in seq])
yaml.add_constructor('!join', join)
但是每次都需要设置一个&VARNAME
,自动设置参数key为可引用对象就好了...
你不能用 YAML 真正做到这一点。对于配置文件,您可以改为使用 ConfigParser,这样您就可以插入值,这样您的示例将如下所示:
root_path: /root
script_path: %(root_path)s/scripts
我只是稍微调整一下 YAML 解析器 ¹:
import ruamel.yaml as yaml
from ruamel.yaml.comments import CommentedMap
yaml_str = """\
root_path: /root # use this key for expansion of values
script_path: root_path + /scripts
"""
def set_item(self, key, value):
split_val = value.split(' + ', 1)
if len(split_val) > 1:
alt_key = split_val[0].strip()
if alt_key in self.keys():
value = self.get(alt_key) + split_val[1]
self._org__setitem__(key, value)
CommentedMap._org__setitem__ = CommentedMap.__setitem__
CommentedMap.__setitem__ = set_item
data = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)
print yaml.dump(data, Dumper=yaml.RoundTripDumper)
会给你:
root_path: /root # use this key for expansion of values
script_path: /root/scripts
请注意,RoundTripLoader/Dumper 组合保留了注释和键的顺序。 data
正常工作 Python dict
.
¹ 这是使用 ruamel.yaml 完成的,我是作者。它是 PyYAML 的超集,主要针对保留注释和数据往返的其他信息。
好吧,我一直在研究解决方案,因为没有什么是我想要的,而且一切都太复杂了,我想死...
此解决方案将 %something%
字符串转换为 something
的值。
完美运行,这是一个例子
root_path: /root
script_path: "%root%/scripts"
使用此方法,script_path
将变为 /root/scripts
。
def replace_values(yaml_file):
def _get(dict, list):
return reduce(lambda d, k: d[k], list, dict)
def _replace(obj):
for k, v in obj.iteritems():
if isinstance(v, dict):
_replace(v)
if isinstance(v, str):
match = re.match(r'%(.*)%', v)
if match:
reference = match.group(1).split('.')
replace = _get(yaml_file, reference)
obj[k] = re.sub(r'%.*%', replace, v)
_replace(yaml_file)
return yaml_file
用法很简单,只需以正常方式加载 Yaml
文件,然后调用 replace。
with open(config_file.get(options.env), 'r') as ymlfile:
config = yaml.load(ymlfile)
config = replace_values(config)
然后我们的新 config
将存储替换的值,当然它不会覆盖原始 .yml
文件。我希望你觉得这很有用,我真的很喜欢,因为这正是我所需要的。
我使用了百分比 %
标记,但您可以将其更改为您想要的任何内容并更改方法以使其与正则表达式一起使用(正则表达式使用一些标记,这就是为什么我使用 %
)