【问题标题】:Sequence as key of YAML mapping in python序列作为python中YAML映射的键
【发布时间】:2012-11-12 08:18:04
【问题描述】:

通过读取 YAML 文件,我想得到以下 python 字典:

mydict = {('x','x'): 4}

或:

mydict = {['x','x']: 4}

基本上,我想要一个元组或列表作为字典中的键。这是我尝试过的 YAML:

mydict:
   !!python/tuple [e,e]: 4 # valid, but python-specific
   !!python/list [b,b]: 4 # error: "found unhashable key"
   [c,c]: 4 # error: "found unhashable key"
   !!seq [d,d]: 4 # error: "found unhashable key"

我正在使用 PyYAML,因此 YAML 代码必须是 YAML1.1 语法,或者您可以向我推荐一种将此类数据存储在文本文件中的更好方法。我想到了json.load,但是 JSON 不接受数组作为映射键。

谢谢 =)

【问题讨论】:

  • 仅供参考:字典键在 Python 中必须是可散列的,因此列表(可以更改,因此没有一致的散列码)不可用。 dicts 和 set 也出来了,虽然 freezesets 没有。但是,元组是不可变的,所以它们可以工作。

标签: python yaml sequence


【解决方案1】:

我还使用 Eclipse/PyDev 下的调试器对此进行了研究,并研究了返回的 YAML 对象的结构。我发现通过将 YAML 对象改造成使用 python 元组而不是列表,我可以在不使用特定于 python 的类型的情况下绕过关于不可哈希键的特定错误,但这只是一种解决方案的错觉。 PyYAML 中字典的键必须是 MappingNode。尽管这可以实现为 Python 值元组,但在内部它仍然被视为一个集合(即受唯一化和重新排序)。这意味着不同的元组(例如 (a,a,b)、(a,b,a)、(b,a) 等)可能最终被视为同一个键,这不是您想要的。

对于信息,这就是我所做的。当遇到类似这样的 YAML 序列时,我定义了一个 Python 类来使用元组而不是列表来构造一个新的 MappingNode:

class Key(yaml.YAMLObject):

  yaml_tag = "tag:example.com:2013:Key"
  @classmethod
  def from_yaml(cls, loader, node):
    if isinstance(node, yaml.SequenceNode):
      new_values = []
      for v in node.value:
        new_values.append( (v, yaml.ScalarNode(tag=u'tag:yaml.org,2002:null'), value=u'')) )
      node = yaml.MappingNode(tag=node.tag, value=tuple(new_values))
    return loader.construct_yaml_object(node,cls)

然后,我在我的 YAML 数据中使用此类作为序列键的键:

!<tag:example.com:2013:Key> [a, b]: 3

或:

%TAG !mytags! tag:example.com:2013:
---
......
  !mytags!Key [a, b]: 3

但是,正如我解释的那样,这并没有按照您期望的方式保留序列。就个人而言,我不会在任何实际应用程序中使用此代码/方法。

对我来说,使用一个集合而不是一个序列似乎更安全,尽管这并不是你所问的。这使用来自Language-Independent Types for YAML Version 1.1set type。我对上面的序列类型使用了类似的方法,但是由于 PyYAML 使用 Python 列表作为 MappingType 的键,所以我只需要将其更改为这样的元组:

class Key(yaml.YAMLObject):

  yaml_tag = "tag:example.com:2013:Key"
  @classmethod
  def from_yaml(cls, loader, node):
    if isinstance(node, yaml.MappingNode):
      node.value = tuple(node.value)
    return loader.construct_yaml_object(node, cls)

并在 YAML 数据中使用集合作为键,如下所示:

!mytags!Key {a, b}: 3

在我看来,这一切都像是 PyYAML 的一个限制:理论上,我认为 YAML 本身没有任何东西不允许将序列用作您想要的键。

【讨论】:

  • 感谢您提供非常完整的反馈!可悲的是,使用集合作为键对我不起作用。仅供参考,我已经不再使用 YAML,而是开始使用 CSV 文件;我的 YAML 代码(以及键和/或值的列表/元组)开始变长。
【解决方案2】:

在 Python 中无法使用列表作为哈希键。它是可变的。你必须使用一个元组。

【讨论】:

  • 好吧,那为什么!!seq [d,d]: 4 不起作用?对复杂映射使用 ? : 语法没有帮助。
猜你喜欢
  • 2018-11-13
  • 2016-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-24
相关资源
最近更新 更多