如何使用pyYAML将Python元组添加到YAML文件中?

15

标题已经很明显了。

当我将一个元组保存到YAML文件时,得到的结果看起来是这样的:

ambient:  !!python/tuple [0.3, 0.3 ,0.3]

当我尝试使用yaml.safe_load(file_object)载入它时,我一直收到一个错误消息:

yaml.constructor.ConstructorError:  could not determine a constructor for the tag 'tag:yaml.org,2002:python/tuple'

需要做什么?

3个回答

18

在pyyaml中,SafeLoader没有包含Python本地类型的加载器,只包括yaml规范中定义的类型。您可以在下面的交互示例中查看SafeLoaderLoader的类型。

您可以定义一个新的Loader类,添加python元组,但不是其他类型,所以它仍然是相当安全的:

import yaml

class PrettySafeLoader(yaml.SafeLoader):
    def construct_python_tuple(self, node):
        return tuple(self.construct_sequence(node))

PrettySafeLoader.add_constructor(
    u'tag:yaml.org,2002:python/tuple',
    PrettySafeLoader.construct_python_tuple)

doc = yaml.dump(tuple("foo bar baaz".split()))
print repr(doc)
thing = yaml.load(doc, Loader=PrettySafeLoader)
print thing

导致:

'!!python/tuple [foo, bar, baaz]\n'
('foo', 'bar', 'baaz')

请参见SafeLoader和Loader类相关的构造函数列表。

>>> yaml.SafeLoader.yaml_constructors
{None: <unbound method SafeConstructor.construct_undefined>,
 u'tag:yaml.org,2002:binary': <unbound method SafeConstructor.construct_yaml_binary>,
 u'tag:yaml.org,2002:bool': <unbound method SafeConstructor.construct_yaml_bool>,
 u'tag:yaml.org,2002:float': <unbound method SafeConstructor.construct_yaml_float>,
 u'tag:yaml.org,2002:int': <unbound method SafeConstructor.construct_yaml_int>,
 u'tag:yaml.org,2002:map': <unbound method SafeConstructor.construct_yaml_map>,
 u'tag:yaml.org,2002:null': <unbound method SafeConstructor.construct_yaml_null>,
 u'tag:yaml.org,2002:omap': <unbound method SafeConstructor.construct_yaml_omap>,
 u'tag:yaml.org,2002:pairs': <unbound method SafeConstructor.construct_yaml_pairs>,
 u'tag:yaml.org,2002:seq': <unbound method SafeConstructor.construct_yaml_seq>,
 u'tag:yaml.org,2002:set': <unbound method SafeConstructor.construct_yaml_set>,
 u'tag:yaml.org,2002:str': <unbound method SafeConstructor.construct_yaml_str>,
 u'tag:yaml.org,2002:timestamp': <unbound method SafeConstructor.construct_yaml_timestamp>}

>>> yaml.Loader.yaml_constructors
{None: <unbound method SafeConstructor.construct_undefined>,
 u'tag:yaml.org,2002:binary': <unbound method SafeConstructor.construct_yaml_binary>,
 u'tag:yaml.org,2002:bool': <unbound method SafeConstructor.construct_yaml_bool>,
 u'tag:yaml.org,2002:float': <unbound method SafeConstructor.construct_yaml_float>,
 u'tag:yaml.org,2002:int': <unbound method SafeConstructor.construct_yaml_int>,
 u'tag:yaml.org,2002:map': <unbound method SafeConstructor.construct_yaml_map>,
 u'tag:yaml.org,2002:null': <unbound method SafeConstructor.construct_yaml_null>,
 u'tag:yaml.org,2002:omap': <unbound method SafeConstructor.construct_yaml_omap>,
 u'tag:yaml.org,2002:pairs': <unbound method SafeConstructor.construct_yaml_pairs>,
 u'tag:yaml.org,2002:python/bool': <unbound method Constructor.construct_yaml_bool>,
 u'tag:yaml.org,2002:python/complex': <unbound method Constructor.construct_python_complex>,
 u'tag:yaml.org,2002:python/dict': <unbound method Constructor.construct_yaml_map>,
 u'tag:yaml.org,2002:python/float': <unbound method Constructor.construct_yaml_float>,
 u'tag:yaml.org,2002:python/int': <unbound method Constructor.construct_yaml_int>,
 u'tag:yaml.org,2002:python/list': <unbound method Constructor.construct_yaml_seq>,
 u'tag:yaml.org,2002:python/long': <unbound method Constructor.construct_python_long>,
 u'tag:yaml.org,2002:python/none': <unbound method Constructor.construct_yaml_null>,
 u'tag:yaml.org,2002:python/str': <unbound method Constructor.construct_python_str>,
 u'tag:yaml.org,2002:python/tuple': <unbound method Constructor.construct_python_tuple>,
 u'tag:yaml.org,2002:python/unicode': <unbound method Constructor.construct_python_unicode>,
 u'tag:yaml.org,2002:seq': <unbound method SafeConstructor.construct_yaml_seq>,
 u'tag:yaml.org,2002:set': <unbound method SafeConstructor.construct_yaml_set>,
 u'tag:yaml.org,2002:str': <unbound method SafeConstructor.construct_yaml_str>,
 u'tag:yaml.org,2002:timestamp': <unbound method SafeConstructor.construct_yaml_timestamp>}

那很有道理。非常感谢! - Louis Thibault

5

至少根据PyYAML文档

函数yaml.safe_load将这种能力限制为简单的Python对象,例如整数或列表。

可以在源代码中看到列表相对更加广泛,但不包括tag:yaml.org,2002:python/tuple

如果您在YAML文件中生成 !!python/tuple 类型,则使用 dump()而不是 safe_dump()。 如果是这种情况,您应该使用 load()代替 safe_load(),因为 dump()创建的文件不能保证可以由 safe_load()加载。(请参见safe_dump()的描述)。


1
我添加了缺失的单词“不”,但我不确定您是否实际上想要反过来表达... - Niklas B.
在什么情况下,一个理智的人会认为使用yaml.loader(而不是safe_loader)是危险的?我正在编写一个加载在YAML文件中定义的资源的游戏。恶意下载可能会覆盖我的YAML文件,从而使我的游戏加载危险代码,但这似乎是最终用户的问题。我无能为力...对吧? - Louis Thibault
@blz YAML 可用于配置和不同项目之间的通信。如果您的 YAML 来自您无法控制的源,则不要使用 yaml.load - Daniel H

3

对于那些正在寻找最新答案的人。

目前,可以通过使用yaml.FullLoader来解决此问题。

import yaml
yaml_file = open("path/to/filename.yaml", 'r')
loaded_yaml = yaml.load(yaml_file, Loader=yaml.FullLoader)

接下来标记为元组的条目将被正确解析,不会出现任何问题。

!!python/tuple [0.3, 0.3 ,0.3]


我知道这不是很安全,但是你的解决方案是最简单和直接的。 - Dave Liu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接