Python Yaml 将 inf 解析为 float

问题描述 投票:0回答:1

在 PyYaml 或 ruamel.yaml 中,我想知道是否有一种方法可以处理特定字符串的解析。具体来说,我希望能够将

"[inf, nan]"
解析为
[float('inf'), float('nan')]
。我还会注意到,我希望
"['inf', 'nan']"
继续解析为
['inf', 'nan']
,所以这只是我想拦截并更改当前行为的未加引号的变体。

我知道目前我可以使用

"[.inf, .nan]"
"[!!float inf, !!float nan]"
,但我很好奇是否可以扩展加载器以允许我期望的语法(但没有)。

也许我只是通过允许“nan”和“inf”被解析为浮点数而不是字符串来制作一个枪 - 并且我有兴趣听到令人信服的理由,我应该允许这种类型的解析。但我不太担心其他解析器会错误地解析我的配置的情况(但也许我低估了将来会造成的痛苦)。我计划使用它作为在命令行上解析参数的一种有效方法,并且我不希望实际的配置文件像这样编写。

无论如何,我仍然对如何做到这一点感兴趣,即使结论是不应该这样做。

python pyyaml ruamel.yaml
1个回答
0
投票

基于我所看到的由

Yes
On
No
Off
造成的混乱 在 YAML 1.1 中解释为布尔值,我认为这不是一个好主意。

但是可以通过更改正则表达式在

ruamel.yaml
和 PyYAML 中执行此操作 识别浮点数(即将隐式标签
tag:yaml.org,2002:float
分配给标量) 然后确保从标量构造浮点数的例程可以处理这些额外的 标量。
ruamel.yaml
中的三个主要改进(与此相关)是: 它对于 YAML 1.1 和 YAML 1.2 解析有不同的正则表达式(后者是默认值, 前者必须通过指令指定,或者通过在
.version
实例上设置
YAML()
来指定); 各个解析器都有这些正则表达式的副本而不是共享 一个(如在 PyYAML 中,这使得在一个程序中拥有多个不同行为的解析器变得困难); 并且正则表达式编译会被延迟,直到真正需要它们为止。

鉴于差异,以下内容仅适用于

ruamel.yaml

您需要创建一个解析器,并将其正则表达式识别替换为all浮点数, 然后创建一个构造函数,根据 公认的标量:

import re, sys
import ruamel.yaml

class NanInfResolver(ruamel.yaml.resolver.VersionedResolver):
    pass

# difference with the regex in resolver.py is the ? after \\.
# as well as recognising N and I as starting chars
NanInfResolver.add_implicit_resolver(
    'tag:yaml.org,2002:float',
    re.compile('''^(?:
     [-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+]?[0-9]+)?
    |[-+]?(?:[0-9][0-9_]*)(?:[eE][-+]?[0-9]+)
    |[-+]?\\.[0-9_]+(?:[eE][-+][0-9]+)?
    |[-+]?\\.?(?:inf|Inf|INF)       
    |\\.?(?:nan|NaN|NAN))$''', re.X),
    list('-+0123456789.niNI')
)

class NanInfConstructor(ruamel.yaml.constructor.RoundTripConstructor):
    def construct_yaml_float(self, node):
        value = self.construct_scalar(node).lower()
        sign = +1
        if value[0] == '-':
            sign = -1
        if value[0] in '+-':
            value_s = value_s[1:]
        if value == 'inf':
            return sign * self.inf_value
        if value == 'nan':
            return self.nan_value
        return super().construct_yaml_float(node)

NanInfConstructor.add_constructor(
    'tag:yaml.org,2002:float', NanInfConstructor.construct_yaml_float
)



yaml_str = """\
[nano, 1.0, .NaN, inf, nan]  # some extra values to test
"""
    
yaml = ruamel.yaml.YAML()
yaml.Resolver = NanInfResolver
yaml.Constructor = NanInfConstructor

data = yaml.load(yaml_str)
for x in data:
    print(type(x), x)
print()
yaml.dump(data, sys.stdout)

给出:

<class 'str'> nano
<class 'ruamel.yaml.scalarfloat.ScalarFloat'> 1.0
<class 'float'> nan
<class 'float'> inf
<class 'float'> nan

[nano, 1.0, .nan, .inf, .nan] # some extra values to test

1.0
加载为
ScalarFloat
是在以下情况下保留其格式所必需的: 倾销。可以以类似的方式保留
.nan
.inf
nan
inf
的不同书写方式,但您会 必须指定一名特别代表,要么延长
ScalarFloat
,要么创建一个 或者保留原始标量字符串值的更显式类型。不管怎样你 将失去使用
x is float('nan')
进行测试的可能性,这可能是一个问题 在实际程序中(这也是 这就是为什么
ruamel.yaml
在往返过程中不保留不同形式的 null 的原因)。

© www.soinside.com 2019 - 2024. All rights reserved.