gpt4 book ai didi

Python 从 YAML 解析类

转载 作者:行者123 更新时间:2023-12-01 02:02:30 25 4
gpt4 key购买 nike

我正在尝试输出然后从 YAML 解析回以下内容

import numpy as np
class MyClass(object):
YAMLTag = '!MyClass'

def __init__(self, name, times, zeros):
self.name = name
self._T = np.array(times)
self._zeros = np.array(zeros)

YAML 文件看起来像

!MyClass:
name: InstanceId
times: [0.0, 0.25, 0.5, 1.0, 2.0, 5.0, 10.0]
zeros: [0.03, 0.03, 0.04, 0.03, 0.03, 0.02, 0.03]

为了编写,我向类添加了两个方法

def toDict(self):
return {'name' : self.name,
'times' : [float(t) for t in self._T],
'zeros' : [float(t) for t in self._zeros]}
@staticmethod
def ToYAML(dumper, data):
return dumper.represent_dict({data.YAMLTag : data.toDict()})

以及阅读方法

@staticmethod
def FromYAML(loader, node):
nodeMap = loader.construct_mapping(node)
return MyClass(name = nodeMap['name'],
times = nodeMap['times'],
zeros = nodeMap['zeros'])

及以下YAML Documentation ,我在同一个 Python 文件 myClass.py 中添加了以下代码片段:

import yaml

yaml.add_constructor(MyClass.YAMLTag, MyClass.FromYAML)
yaml.add_representer(MyClass, MyClass.ToYAML)
<小时/>

现在,书写似乎没问题,但是阅读 YAML、代码

loader.construct_mapping(node)

似乎返回带有空数据的字典:

{'zeros': [], 'name': 'InstanceId', 'times': []}

我应该如何修复阅读器才能正确执行此操作?或者也许我没有写出一些东西,对吗?我花了很长时间查看 PyYAML 文档并调试包的实现方式,但无法找到解析复杂结构的方法,而我似乎找到的唯一示例有一个可以轻松解析的 1 行类。

<小时/>

相关:YAML parsing and Python

<小时/>

更新

按如下方式手动解析节点有效:

name, times, zeros = None, None, None
for key, value in node.value:
elementName = loader.construct_scalar(key)
if elementName == 'name':
name = loader.construct_scalar(value)
elif elementName == 'times':
times = loader.construct_sequence(value)
elif elementName == 'zeros':
zeros = loader.construct_sequence(value)
else:
raise ValueError('Unexpected YAML key %s' % elementName)

但问题仍然存在,有没有非手动的方法来做到这一点?

最佳答案

即使没有考虑到您应该阅读PEP 8, the style guide for Python code,您的方法也存在多个问题。 ,特别是 Method Names and Instance Variables 上的部分

  1. 正如您所指出的,您已经仔细阅读了 Python 文档,您不可能没有注意到 yaml.load()是不安全的。几乎从来没有必要使用它,如果您编写自己的表示器和构造函数,则当然不需要使用它。

  2. 您使用dumper.represent_dict({data.YAMLTag : data.toDict()})它将对象转储为键值对。您想要做的(至少如果您想在输出 YAML 中包含标签)是:dumper.represent_mapping(data.YAMLTag, data.toDict()) 。这将为您提供以下形式的输出:

    !MyClass
    name: InstanceId
    times: [0.0, 0.25, 0.5, 1.0, 2.0, 5.0, 10.0]
    zeros: [0.03, 0.03, 0.04, 0.03, 0.03, 0.02, 0.03]

    即标记的映射而不是键值对,其中值是映射。 (我预计第一行是 '!MyClass': 以确保以感叹号开头的标量不会被解释为标签)。

  3. 构造一个可能是自引用(直接或间接)的复杂对象必须在 two steps 中完成。使用生成器(PyYAML 代码以正确的方式调用它)。在您的代码中,您假设您拥有创建 MyClass 实例的所有参数。 。但如果存在自引用,这些参数必须包含该实例本身,并且该实例尚未创建。 YAML 代码库中正确的示例代码是 construct_yaml_object()constructor.py :

    def construct_yaml_object(self, node, cls):
    data = cls.__new__(cls)
    yield data
    if hasattr(data, '__setstate__'):
    state = self.construct_mapping(node, deep=True)
    data.__setstate__(state)
    else:
    state = self.construct_mapping(node)
    data.__dict__.update(state)

    您不必使用.__new__() ,但你应该采取 deep=True考虑到解释here

一般来说,拥有 __repr__() 也很有用。它允许您检查加载的对象,其内容比 <__main__.MyClass object at 0x12345> 更具表现力

进口:

from __future__ import print_function

import sys
import yaml
from cStringIO import StringIO
import numpy as np

为了检查自引用版本的正确工作,我添加了 self._ref类的属性:

class MyClass(object):
YAMLTag = u'!MyClass'

def __init__(self, name=None, times=[], zeros=[], ref=None):
self.update(name, times, zeros, ref)

def update(self, name, times, zeros, ref):
self.name = name
self._T = np.array(times)
self._zeros = np.array(zeros)
self._ref = ref

def toDict(self):
return dict(name=self.name,
times=self._T.tolist(),
zeros=self._zeros.tolist(),
ref=self._ref,
)

def __repr__(self):
return "{}(name={}, times={}, zeros={})".format(
self.__class__.__name__,
self.name,
self._T.tolist(),
self._zeros.tolist(),
)

def update_self_ref(self, ref):
self._ref = ref

表示者和构造函数“方法”:

    @staticmethod
def to_yaml(dumper, data):
return dumper.represent_mapping(data.YAMLTag, data.toDict())

@staticmethod
def from_yaml(loader, node):
value = MyClass()
yield value
node_map = loader.construct_mapping(node, deep=True)
value.update(**node_map)


yaml.add_representer(MyClass, MyClass.to_yaml, Dumper=yaml.SafeDumper)
yaml.add_constructor(MyClass.YAMLTag, MyClass.from_yaml, Loader=yaml.SafeLoader)

以及如何使用它:

instance = MyClass('InstanceId',
[0.0, 0.25, 0.5, 1.0, 2.0, 5.0, 10.0],
[0.03, 0.03, 0.04, 0.03, 0.03, 0.02, 0.03])
instance.update_self_ref(instance)

buf = StringIO()
yaml.safe_dump(instance, buf)

yaml_str = buf.getvalue()
print(yaml_str)


data = yaml.safe_load(yaml_str)
print(data)
print(id(data), id(data._ref))

以上组合得出:

&id001 !MyClass
name: InstanceId
ref: *id001
times: [0.0, 0.25, 0.5, 1.0, 2.0, 5.0, 10.0]
zeros: [0.03, 0.03, 0.04, 0.03, 0.03, 0.02, 0.03]

MyClass(name=InstanceId, times=[0.0, 0.25, 0.5, 1.0, 2.0, 5.0, 10.0], zeros=[0.03, 0.03, 0.04, 0.03, 0.03, 0.02, 0.03])
139737236881744 139737236881744

如您所见 iddatadata._ref加载后是一样的。

如果您在构造函数中使用简单的方法(仅使用loader.construct_mapping(node, deep=True)),上面的代码会抛出错误。

关于Python 从 YAML 解析类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49453087/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com